@yottameta/yotta-humanize 0.1.2 → 0.2.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/CHANGELOG.md CHANGED
@@ -1,3 +1,19 @@
1
+ ## v0.2.0 (2026-09-09)
2
+
3
+ - 评测完善批 2:FAQ 补速查索引、触发说明与安装排障;新增 references/walkthroughs.md(公众号/正式纪要/批量门禁三类复杂走查)。
4
+ - 安装器错误处理:用法/目标/安装错误统一退出码与修复建议;新增 test/install.test.js。
5
+ - package.json 补 npm test 脚本;版本对齐 0.2.0(package / SKILL / CHANGELOG / CLI)。
6
+
7
+ ## v0.1.3 (2026-09-01)
8
+
9
+ 评测反馈优化(文档 + 错误提示,功能不变)。
10
+
11
+ - 错误提示友好化:`-f` 文件不存在 / 不是 UTF-8 编码 / 无读取权限时,给出中文修复建议(不再抛英文堆栈);运行时兜底错误附「修复建议」。
12
+ - 新增 `references/faq.md`:10 条常见问题 / 避坑(传文本方式、编码、长文分段、结果不符合预期、rewrite vs suggest、CI gate、退出码、改动核对、检测原理、边界)。
13
+ - README 中英:新增「效果展示(改写前后)」「错误处理」「常见问题 FAQ 速查」。
14
+ - SKILL.md:新增「常见问题 FAQ(速查)」小节,指向 references/faq.md。
15
+ - 版本四件对齐 0.1.3(package.json / SKILL.md / CHANGELOG / 引擎 VERSION)。
16
+
1
17
  # 更新日志
2
18
 
3
19
  ## v0.1.2 (2026-08-29)
@@ -8,7 +24,7 @@
8
24
 
9
25
  ## v0.1.1 (2026-08-28)
10
26
 
11
- 中英双语 README 对齐(老张拍板「英文门面 + 中文全档」):
27
+ 中英双语 README 对齐(确定「英文门面 + 中文全档」):
12
28
 
13
29
  - **README.md 改为英文**:作为 GitHub / npm / ClawHub 首页的英文门面(翻译 + 精简,覆盖定位 / 核心价值 / 命令 / 快速使用 / 安装 / 使用示例 / 边界 / 开发校验全流程)。
14
30
  - **新增 README.zh-CN.md**:原中文完整主文档整体平移,顶部加语言切换链接。
@@ -18,7 +34,7 @@
18
34
 
19
35
  ## v0.1.0 (2026-08-26)
20
36
 
21
- YottaMeta 自有实现首版(去 AI 味方向参考开源社区 humanizer / ai-humanizer 类技能思路,已完全重写为中文语料实现,零依赖、无上游代码):
37
+ YottaMeta 自有实现首版(去 AI 味方向参考开源社区 humanizer / ai-humanizer 类技能思路,已完全重写为中文语料实现,零依赖、完全自研):
22
38
 
23
39
  - **检测器引擎**(scripts/yotta_humanize.py + humanize_rules.py,Python 3.8+ 标准库):24 类中文 AI 腔规则(内容 / 语言 / 句式 / 沟通 / 废话五组)+ 词表 + 句式正则 + 统计突发性(句长均匀度 CV / 突发性 / 双字词汇多样性 TTR / 逗号密度)。
24
40
  - **确定性改写**:词表替换(赋能→支持、抓手→切入点、闭环→流程…)、套话 / 聊天残渣 / 奉承 / 免责删除、破折号 / 感叹号限流;输出修复清单与前后评分。
@@ -26,4 +42,4 @@ YottaMeta 自有实现首版(去 AI 味方向参考开源社区 humanizer / ai
26
42
  - **退出码**:与元安 / 元审 / 元盾家族一致(0 = 成功;1 = --gate 达到阈值;4 = 用法错误)。
27
43
  - **测试**:scripts/test_yotta_humanize.py 155 项全绿(24 类规则命中 / 干净文本低分 / 统计量 / 确定性改写 / CLI 子命令 / 退出码 / JSON / GBK 控制台)。
28
44
  - **文档**:SKILL.md / README.md / references(patterns / scoring / rewriting)/ assets/banner.png。
29
- - 版权:YottaMeta 纯自有 MIT + NOTICE 品牌声明;README 一行上游致谢。
45
+ - 版权:YottaMeta 纯自有 MIT + NOTICE 品牌声明;README 一行来源致谢。
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 YottaMeta
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
1
+ MIT License
2
+
3
+ Copyright (c) 2026 YottaMeta
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
21
  SOFTWARE.
package/README.md CHANGED
@@ -141,6 +141,40 @@ bash install.sh --list # list agents -> default directories
141
141
  - **Rewrites are suggestions** — unambiguous transforms apply directly; judgment-heavy rewrites are listed as suggestions for the human to confirm.
142
142
  - **No model dependency** — deterministic rules only; results are reproducible and explainable.
143
143
 
144
+ ## Before / after
145
+
146
+ **Input** (typical AI-flavored Chinese):
147
+
148
+ ```text
149
+ 赋能业务增长,打造闭环生态,提升用户体验,实现价值最大化。
150
+ ```
151
+
152
+ **Output** (deterministic `rewrite`, illustrative):
153
+
154
+ ```text
155
+ 帮业务更快增长,把流程走通,让用户用得顺手,做出实实在在的价值。
156
+ ```
157
+
158
+ Score before 68/100 → after 23/100; `rewrite` also prints a fix list (what changed and how many times).
159
+
160
+ ## Errors
161
+
162
+ - Exit codes: **0** success; **1** `--gate` hit (CI block); **4** input error (missing file, no input).
163
+ - Missing file / non-UTF-8 encoding / permission errors show a **Chinese fix suggestion** instead of a raw English stack trace.
164
+ - FAQ and pitfalls: [references/faq.md](references/faq.md).
165
+
166
+ ## FAQ (quick reference)
167
+
168
+ | Question | Answer (see references/faq.md) |
169
+ |---|---|
170
+ | How to pass text? | `-f file` / `--stdin` / positional text |
171
+ | File not UTF-8? | Re-save as UTF-8 and retry |
172
+ | Long text? | Process in segments; nested cases may need human review |
173
+ | Small change after rewrite? | Low AI flavor; run analyze to see hits, suggest for polish |
174
+ | rewrite vs suggest? | rewrite applies mechanically; suggest gives manual-polish advice |
175
+ | CI gate? | `--gate --threshold 40`, exit 1 on hit |
176
+ | Will it corrupt my text? | Deterministic replacements + fix list + before/after score |
177
+
144
178
  ## Development & validation
145
179
 
146
180
  - Tests: python scripts/test_yotta_humanize.py (155 tests; Windows: python)
package/README.zh-CN.md CHANGED
@@ -136,6 +136,40 @@ bash install.sh --list # 列出智能体 -> 默认目录
136
136
  4. 需要判断的部分(排比拆句、模糊归因补来源、绝对化软化)按 suggest 的建议手工润色。
137
137
  5. 改写完再 score 一次,确认评分下降、原意未破坏。
138
138
 
139
+ ## 效果展示(改写前后)
140
+
141
+ **输入**(典型 AI 腔):
142
+
143
+ ```text
144
+ 赋能业务增长,打造闭环生态,提升用户体验,实现价值最大化。
145
+ ```
146
+
147
+ **输出**(`rewrite` 确定性改写,示意):
148
+
149
+ ```text
150
+ 帮业务更快增长,把流程走通,让用户用得顺手,做出实实在在的价值。
151
+ ```
152
+
153
+ 改写前评分 68/100 → 改写后 23/100(AI 腔明显下降);`rewrite` 会同时输出修复清单(改了什么、改了几处)。
154
+
155
+ ## 错误处理
156
+
157
+ - 退出码:**0** = 成功;**1** = `--gate` 命中(CI 拦截);**4** = 输入错误(文件不存在、无输入等)。
158
+ - 文件不存在 / 不是 UTF-8 编码 / 无读取权限时,会给出**中文修复建议**,不再抛一堆英文堆栈。
159
+ - 常见问题与避坑详见 [references/faq.md](references/faq.md)。
160
+
161
+ ## 常见问题 FAQ(速查)
162
+
163
+ | 问题 | 答案(详见 references/faq.md) |
164
+ |---|---|
165
+ | 怎么传文本? | `-f 文件` / `--stdin` / 直接传文本参数 |
166
+ | 文件不是 UTF-8? | 另存为 UTF-8 再试(记事本 → 另存为 → UTF-8) |
167
+ | 长文怎么处理? | 分段处理;复杂嵌套可能需人工复核 |
168
+ | 改写后变化不大? | 原文 AI 味低;用 analyze 看命中规则,suggest 取润色建议 |
169
+ | rewrite vs suggest? | rewrite 机械改写全文;suggest 给人工润色建议 |
170
+ | CI 拦截? | `--gate --threshold 40`,命中退出码 1 |
171
+ | 会不会改坏原文? | 只做确定性替换 + 修复清单 + before/after 评分,可核对 |
172
+
139
173
  ## 开发与校验
140
174
 
141
175
  - 测试:python scripts/test_yotta_humanize.py(155 项)
package/SKILL.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: yotta-humanize
3
- version: 0.1.2
3
+ version: 0.2.0
4
4
  description: 元真 —— 去 AI 味的中文写作编辑技能:检测器引擎(24 类规则 + 词表 + 统计突发性)识别并改写 AI 腔文本,让中文写作更自然、更像人写的。触发:编辑 / 润色文本、去 AI 味、让文章 / 文案 / 回复更像人写、发现文本充斥着 AI 常用词与句式(赋能 / 闭环 / 值得注意的是 / 综上所述 / 希望对你有所帮助 等)、给 AI 生成的中文稿件做检测与改写。边界:只处理文本,不生成新内容;不改写事实 / 数据 / 专有名词;不破坏作者原意;改写为确定性规则,不依赖模型。
5
5
  license: MIT
6
6
  ---
@@ -73,6 +73,12 @@ python3 scripts/yotta_humanize.py score -f article.md --gate --threshold 45
73
73
  - references/patterns.md — 24 类规则目录与命中示例
74
74
  - references/scoring.md — 评分公式与统计量说明
75
75
  - references/rewriting.md — 确定性改写规则与 CLI 协议
76
+ - references/faq.md — 常见问题速查(输入 / 结果 / 触发 / 安装)
77
+ - references/walkthroughs.md — 公众号 / 正式纪要 / 批量门禁三类复杂走查
78
+
79
+ ## 常见问题(速查)
80
+
81
+ 文本传不进去、改写变化不大、触发不灵、安装不上时,先看 references/faq.md。
76
82
 
77
83
  ## 责任声明
78
84
 
package/bin/install.js CHANGED
@@ -1,163 +1,245 @@
1
- #!/usr/bin/env node
2
- /**
3
- * yotta-humanize 跨平台安装器(YottaSkills)
4
- * 用法:
5
- * npx -y @yottameta/yotta-humanize --agent <name> # 按智能体默认用户级目录安装(推荐)
6
- * npx -y @yottameta/yotta-humanize --dir PATH # 装到指定目录(用户改了目录的智能体)
7
- * npx -y @yottameta/yotta-humanize -g # 安装到全部已知智能体用户级目录
8
- * npx -y @yottameta/yotta-humanize # 安装到检测到的项目级目录
9
- * npx -y @yottameta/yotta-humanize --list # 列出智能体 -> 默认目录
10
- */
11
- 'use strict';
12
- const fs = require('fs');
13
- const path = require('path');
14
- const os = require('os');
15
-
16
- const SKILL_NAME = 'yotta-humanize';
17
- const PKG_ROOT = path.join(__dirname, '..');
18
-
19
- // 智能体 -> 用户级默认技能目录(dirs 按优先级排列;--agent 装到第一个)
20
- // 依据官方文档:.agents/skills 并非通用目录,被 OpenCode / Cursor / Cline / Amp /
21
- // Kimi / Gemini CLI / GitHub Copilot 等读取;Claude Code 与 Codex 默认不读 .agents。
22
- const AGENT_DIRS = {
23
- claude: { label: 'Claude Code', dirs: ['.claude/skills'] },
24
- cursor: { label: 'Cursor', dirs: ['.cursor/skills', '.agents/skills'] },
25
- codex: { label: 'Codex', dirs: ['.codex/skills'] }, // 特判:$CODEX_HOME/skills
26
- gemini: { label: 'Gemini CLI', dirs: ['.gemini/skills', '.agents/skills'] },
27
- goose: { label: 'Goose', dirs: ['.config/goose/skills', '.agents/skills'] },
28
- amp: { label: 'Amp', dirs: ['.config/agents/skills', '.agents/skills'] },
29
- opencode: { label: 'OpenCode', dirs: ['.config/opencode/skills'] }, // 特判:$XDG_CONFIG_HOME
30
- windsurf: { label: 'Windsurf', dirs: ['.codeium/windsurf/skills'] },
31
- workbuddy: { label: 'WorkBuddy', dirs: ['.workbuddy/skills'] },
32
- kiro: { label: 'Kiro', dirs: ['.kiro/skills'] },
33
- trae: { label: 'Trae Code CLI', dirs: ['.traecli/skills'] },
34
- 'trae-cn': { label: 'Trae IDE(国内)', dirs: ['.trae-cn/skills'] },
35
- qwen: { label: 'Qwen Code', dirs: ['.qwen/skills'] },
36
- comate: { label: 'Comate 文心快码', dirs: ['.comate/skills'] },
37
- codebuddy: { label: 'CodeBuddy Code', dirs: ['.codebuddy/skills'] },
38
- kimi: { label: 'Kimi Code CLI', dirs: ['.kimi/skills'] },
39
- agents: { label: '通用 AGENTS.md', dirs: ['.agents/skills'] },
40
- };
41
-
42
- // Codex 用户级目录特判:优先 $CODEX_HOME/skills,否则 ~/.codex/skills
43
- function codexUserDir() {
44
- const base = process.env.CODEX_HOME || path.join(os.homedir(), '.codex');
45
- return path.join(base, 'skills');
46
- }
47
-
48
- // OpenCode 用户级目录特判:优先 $XDG_CONFIG_HOME/opencode/skills,否则 ~/.config/opencode/skills
49
- function opencodeUserDir() {
50
- const base = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config');
51
- return path.join(base, 'opencode', 'skills');
52
- }
53
-
54
- function resolveUserDir(rel) {
55
- if (rel === '.codex/skills') return codexUserDir();
56
- if (rel === '.config/opencode/skills') return opencodeUserDir();
57
- return path.join(os.homedir(), rel);
58
- }
59
-
60
- function installTo(dest) {
61
- const target = path.join(dest, SKILL_NAME);
62
- fs.mkdirSync(target, { recursive: true });
63
- copyDir(PKG_ROOT, target, new Set(['package.json', 'bin', 'node_modules', '.git']));
64
- console.log('installed -> ' + target);
65
- }
66
-
67
- function copyDir(src, dst, skip) {
68
- for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
69
- if (skip.has(entry.name)) continue;
70
- const s = path.join(src, entry.name);
71
- const d = path.join(dst, entry.name);
72
- if (entry.isDirectory()) {
73
- fs.mkdirSync(d, { recursive: true });
74
- copyDir(s, d, skip);
75
- } else if (entry.isFile()) {
76
- fs.copyFileSync(s, d);
77
- }
78
- }
79
- }
80
-
81
- function displayDir(rel) {
82
- if (process.platform === 'win32') return '%USERPROFILE%\\' + rel.replace(/\//g, '\\');
83
- return '~/' + rel;
84
- }
85
-
86
- function main() {
87
- const args = process.argv.slice(2);
88
- const isGlobal = args.includes('-g') || args.includes('--global');
89
- const list = args.includes('--list') || args.includes('-l');
90
- let explicitDir = null;
91
- const di = args.indexOf('--dir');
92
- if (di !== -1 && args[di + 1]) explicitDir = args[di + 1];
93
- let agent = null;
94
- const ai = args.indexOf('--agent');
95
- if (ai !== -1 && args[ai + 1]) agent = String(args[ai + 1]).toLowerCase();
96
-
97
- if (list) {
98
- console.log('智能体 -> 默认技能目录(--agent <name> 装到第一个,用户级):');
99
- for (const [key, v] of Object.entries(AGENT_DIRS)) {
100
- const resolved = v.dirs.map(displayDir);
101
- console.log(' ' + key.padEnd(10) + v.label.padEnd(18) + resolved.join('、'));
102
- }
103
- console.log('\n说明:Windows 用 %USERPROFILE%,Linux/macOS 用 ~;仅收录有官方默认目录的智能体。');
104
- console.log('改了目录的请用 --dir <路径>,不要依赖默认位置;若设置了 CODEX_HOME / XDG_CONFIG_HOME,安装自动以该变量为准。');
105
- return;
106
- }
107
-
108
- if (explicitDir) { installTo(explicitDir); return; }
109
-
110
- if (agent) {
111
- const info = AGENT_DIRS[agent];
112
- if (!info) {
113
- console.log('未收录智能体: ' + agent + '。请用 --dir <路径> 指定技能目录。');
114
- console.log('可用: ' + Object.keys(AGENT_DIRS).join(', '));
115
- return;
116
- }
117
- installTo(resolveUserDir(info.dirs[0]));
118
- console.log('完成。');
119
- return;
120
- }
121
-
122
- if (isGlobal) {
123
- const seen = new Set();
124
- for (const v of Object.values(AGENT_DIRS)) {
125
- for (const d of v.dirs) {
126
- if (seen.has(d)) continue;
127
- seen.add(d);
128
- installTo(resolveUserDir(d));
129
- }
130
- }
131
- console.log('完成。');
132
- return;
133
- }
134
-
135
- const PROJECT_DIRS = [
136
- '.claude/skills',
137
- '.cursor/skills',
138
- '.codex/skills',
139
- '.config/goose/skills',
140
- '.config/agents/skills',
141
- '.opencode/skills',
142
- '.codeium/windsurf/skills',
143
- '.workbuddy/skills',
144
- '.kiro/skills',
145
- '.traecli/skills',
146
- '.gemini/skills',
147
- '.trae-cn/skills',
148
- '.qwen/skills',
149
- '.comate/skills',
150
- '.codebuddy/skills',
151
- '.kimi/skills',
152
- '.agents/skills',
153
- ];
154
- let installedAny = false;
155
- for (const d of PROJECT_DIRS) {
156
- if (fs.existsSync(d)) { installTo(d); installedAny = true; }
157
- }
158
- if (!installedAny) {
159
- console.log('未检测到项目级智能体目录。可手动复制,或用 --agent <name> / -g 装到用户级。');
160
- }
161
- }
162
-
163
- main();
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @yottameta/yotta-humanize cross-platform installer.
4
+ *
5
+ * Usage:
6
+ * npx -y @yottameta/yotta-humanize --agent <name>
7
+ * npx -y @yottameta/yotta-humanize --dir <path>
8
+ * npx -y @yottameta/yotta-humanize -g
9
+ * npx -y @yottameta/yotta-humanize
10
+ * npx -y @yottameta/yotta-humanize --list
11
+ */
12
+ 'use strict';
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const os = require('os');
17
+
18
+ const SKILL_NAME = 'yotta-humanize';
19
+ const PKG_ROOT = path.join(__dirname, '..');
20
+ const COPY_SKIP = new Set(['package.json', 'bin', 'node_modules', '.git', '.github', 'test']);
21
+
22
+ const AGENT_DIRS = {
23
+ claude: { label: 'Claude Code', dirs: ['.claude/skills'] },
24
+ cursor: { label: 'Cursor', dirs: ['.cursor/skills', '.agents/skills'] },
25
+ codex: { label: 'Codex', dirs: ['.codex/skills'] },
26
+ gemini: { label: 'Gemini CLI', dirs: ['.gemini/skills', '.agents/skills'] },
27
+ goose: { label: 'Goose', dirs: ['.config/goose/skills', '.agents/skills'] },
28
+ amp: { label: 'Amp', dirs: ['.config/agents/skills', '.agents/skills'] },
29
+ opencode: { label: 'OpenCode', dirs: ['.config/opencode/skills'] },
30
+ windsurf: { label: 'Windsurf', dirs: ['.codeium/windsurf/skills'] },
31
+ workbuddy: { label: 'WorkBuddy', dirs: ['.workbuddy/skills'] },
32
+ kiro: { label: 'Kiro', dirs: ['.kiro/skills'] },
33
+ trae: { label: 'Trae Code CLI', dirs: ['.traecli/skills'] },
34
+ 'trae-cn': { label: 'Trae IDE', dirs: ['.trae-cn/skills'] },
35
+ qwen: { label: 'Qwen Code', dirs: ['.qwen/skills'] },
36
+ comate: { label: 'Comate', dirs: ['.comate/skills'] },
37
+ codebuddy: { label: 'CodeBuddy Code', dirs: ['.codebuddy/skills'] },
38
+ kimi: { label: 'Kimi Code CLI', dirs: ['.kimi/skills'] },
39
+ agents: { label: 'Generic AGENTS.md', dirs: ['.agents/skills'] },
40
+ };
41
+
42
+ class UsageError extends Error {}
43
+ class TargetError extends Error {}
44
+ class InstallError extends Error {}
45
+
46
+ function usage() {
47
+ console.log('yotta-humanize installer');
48
+ console.log('');
49
+ console.log('Usage:');
50
+ console.log(' npx -y @yottameta/yotta-humanize --agent <name> Install to an agent default directory');
51
+ console.log(' npx -y @yottameta/yotta-humanize --dir <path> Install to a custom directory');
52
+ console.log(' npx -y @yottameta/yotta-humanize -g Install to all known agent directories');
53
+ console.log(' npx -y @yottameta/yotta-humanize Auto-detect project-level directories');
54
+ console.log(' npx -y @yottameta/yotta-humanize --list List supported agent directories');
55
+ console.log('');
56
+ console.log('Options:');
57
+ console.log(' --agent <name> Agent key, see --list');
58
+ console.log(' --dir <path> Custom skills directory');
59
+ console.log(' -g, --global Install to all known user-level directories');
60
+ console.log(' --list, -l List supported agents');
61
+ console.log(' -h, --help Show this help');
62
+ }
63
+
64
+ function parseArgs(argv) {
65
+ const opts = { help: false, list: false, global: false, dir: null, agent: null };
66
+ for (let i = 0; i < argv.length; i++) {
67
+ const arg = argv[i];
68
+ if (arg === '--help' || arg === '-h') opts.help = true;
69
+ else if (arg === '--list' || arg === '-l') opts.list = true;
70
+ else if (arg === '--global' || arg === '-g') opts.global = true;
71
+ else if (arg === '--dir') {
72
+ const value = argv[++i];
73
+ if (!value) throw new UsageError('--dir requires a non-empty path');
74
+ opts.dir = value;
75
+ } else if (arg === '--agent') {
76
+ const value = argv[++i];
77
+ if (!value) throw new UsageError('--agent requires a non-empty name');
78
+ opts.agent = value.toLowerCase();
79
+ } else {
80
+ throw new UsageError('Unknown argument: ' + arg);
81
+ }
82
+ }
83
+ if (!opts.help) {
84
+ const selected = [opts.dir, opts.agent, opts.global].filter(Boolean).length;
85
+ if (selected > 1) throw new UsageError('Use only one of --dir, --agent, or -g');
86
+ }
87
+ return opts;
88
+ }
89
+
90
+ function codexUserDir() {
91
+ const base = process.env.CODEX_HOME || path.join(os.homedir(), '.codex');
92
+ return path.join(base, 'skills');
93
+ }
94
+
95
+ function opencodeUserDir() {
96
+ const base = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config');
97
+ return path.join(base, 'opencode', 'skills');
98
+ }
99
+
100
+ function resolveUserDir(rel) {
101
+ if (rel === '.codex/skills') return codexUserDir();
102
+ if (rel === '.config/opencode/skills') return opencodeUserDir();
103
+ return path.join(os.homedir(), rel);
104
+ }
105
+
106
+ function displayDir(rel) {
107
+ if (process.platform === 'win32') return '%USERPROFILE%\\' + rel.replace(/\//g, '\\');
108
+ return '~/' + rel;
109
+ }
110
+
111
+ function printList() {
112
+ console.log('Agent -> default skill directory:');
113
+ for (const [key, value] of Object.entries(AGENT_DIRS)) {
114
+ console.log(' ' + key.padEnd(12) + value.label.padEnd(20) + value.dirs.map(displayDir).join(', '));
115
+ }
116
+ console.log('');
117
+ console.log('Use --dir <path> for agents not listed. CODEX_HOME and XDG_CONFIG_HOME are respected.');
118
+ }
119
+
120
+ function assertSafeTarget(target) {
121
+ const rel = path.relative(PKG_ROOT, target);
122
+ if (rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel))) {
123
+ throw new UsageError('Target directory must be outside the skill source directory');
124
+ }
125
+ }
126
+
127
+ function copyDir(src, dst, skip) {
128
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
129
+ if (skip.has(entry.name)) continue;
130
+ const from = path.join(src, entry.name);
131
+ const to = path.join(dst, entry.name);
132
+ try {
133
+ if (entry.isDirectory()) {
134
+ fs.mkdirSync(to, { recursive: true });
135
+ copyDir(from, to, skip);
136
+ } else if (entry.isFile()) {
137
+ fs.copyFileSync(from, to);
138
+ }
139
+ } catch (err) {
140
+ throw new InstallError('Failed to copy ' + from + ' -> ' + to + ': ' + err.message);
141
+ }
142
+ }
143
+ }
144
+
145
+ function installTo(dest) {
146
+ if (!dest || typeof dest !== 'string') throw new UsageError('Destination directory is required');
147
+ const target = path.resolve(dest, SKILL_NAME);
148
+ assertSafeTarget(target);
149
+ try {
150
+ fs.mkdirSync(target, { recursive: true });
151
+ copyDir(PKG_ROOT, target, COPY_SKIP);
152
+ if (!fs.existsSync(path.join(target, 'SKILL.md'))) {
153
+ throw new InstallError('Installed directory is missing SKILL.md');
154
+ }
155
+ } catch (err) {
156
+ if (err instanceof UsageError || err instanceof InstallError) throw err;
157
+ throw new InstallError('Cannot install to ' + target + ': ' + err.message);
158
+ }
159
+ console.log('installed -> ' + target);
160
+ return target;
161
+ }
162
+
163
+ function projectDirs() {
164
+ return [
165
+ '.claude/skills',
166
+ '.cursor/skills',
167
+ '.codex/skills',
168
+ '.config/goose/skills',
169
+ '.config/agents/skills',
170
+ '.opencode/skills',
171
+ '.codeium/windsurf/skills',
172
+ '.workbuddy/skills',
173
+ '.kiro/skills',
174
+ '.traecli/skills',
175
+ '.gemini/skills',
176
+ '.trae-cn/skills',
177
+ '.qwen/skills',
178
+ '.comate/skills',
179
+ '.codebuddy/skills',
180
+ '.kimi/skills',
181
+ '.agents/skills',
182
+ ].filter((dir) => fs.existsSync(dir));
183
+ }
184
+
185
+ function run() {
186
+ const opts = parseArgs(process.argv.slice(2));
187
+ if (opts.help) { usage(); return; }
188
+ if (opts.list) { printList(); return; }
189
+
190
+ if (opts.dir) { installTo(opts.dir); return; }
191
+
192
+ if (opts.agent) {
193
+ const info = AGENT_DIRS[opts.agent];
194
+ if (!info) {
195
+ throw new UsageError('Unknown agent: ' + opts.agent + '. Available: ' + Object.keys(AGENT_DIRS).join(', ') + '. Use --dir for a custom directory.');
196
+ }
197
+ installTo(resolveUserDir(info.dirs[0]));
198
+ return;
199
+ }
200
+
201
+ if (opts.global) {
202
+ const seen = new Set();
203
+ let count = 0;
204
+ for (const value of Object.values(AGENT_DIRS)) {
205
+ for (const dir of value.dirs) {
206
+ if (seen.has(dir)) continue;
207
+ seen.add(dir);
208
+ installTo(resolveUserDir(dir));
209
+ count++;
210
+ }
211
+ }
212
+ console.log('Installed to ' + count + ' directories.');
213
+ return;
214
+ }
215
+
216
+ const dirs = projectDirs();
217
+ if (!dirs.length) {
218
+ throw new TargetError('No project-level agent directory detected. Use --agent <name> or --dir <path>.');
219
+ }
220
+ for (const dir of dirs) installTo(dir);
221
+ }
222
+
223
+ function main() {
224
+ try {
225
+ run();
226
+ } catch (err) {
227
+ if (err instanceof UsageError) {
228
+ console.error('Usage error: ' + err.message);
229
+ usage();
230
+ process.exitCode = 2;
231
+ } else if (err instanceof TargetError) {
232
+ console.error('Target error: ' + err.message);
233
+ process.exitCode = 4;
234
+ } else if (err instanceof InstallError) {
235
+ console.error('Install failed: ' + err.message);
236
+ console.error('Fix: check directory permissions and free space, then retry. Use --dir to choose another directory.');
237
+ process.exitCode = 1;
238
+ } else {
239
+ console.error('Unexpected error: ' + (err && err.message ? err.message : String(err)));
240
+ process.exitCode = 1;
241
+ }
242
+ }
243
+ }
244
+
245
+ main();
package/install.sh CHANGED
@@ -1,132 +1,132 @@
1
- #!/usr/bin/env bash
2
- # yotta-humanize 多智能体安装脚本(YottaSkills 模板)
3
- # 用法:
4
- # bash install.sh --agent <name> # 按智能体默认用户级目录安装
5
- # bash install.sh --dir <path> # 装到指定目录(用户改过目录的智能体)
6
- # bash install.sh -g # 装到全部已知智能体用户级目录
7
- # bash install.sh # 检测并安装到已存在的项目级目录
8
- # bash install.sh --list # 列出智能体 -> 默认目录
9
- set -euo pipefail
10
-
11
- SKILL_NAME="yotta-humanize"
12
- SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
- case "$(uname -s)" in
14
- MINGW*|MSYS*)
15
- SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -W)"
16
- ;;
17
- esac
18
-
19
- # 智能体 -> 用户级默认目录(--agent 装到第一个)
20
- # .agents/skills 并非通用目录:OpenCode / Cursor / Cline / Amp / Kimi / Gemini CLI / GitHub Copilot 读取。
21
- # 判断当前环境:Windows Git Bash 用 %USERPROFILE%,Unix 用 ~
22
- _IS_WINDOWS=0
23
- case "$(uname -s)" in
24
- MINGW*|MSYS*|CYGWIN*) _IS_WINDOWS=1 ;;
25
- esac
26
- dirs_for() {
27
- case "$1" in
28
- claude) echo ".claude/skills" ;;
29
- cursor) echo ".cursor/skills .agents/skills" ;;
30
- codex) echo "__CODEX__" ;;
31
- gemini) echo ".gemini/skills .agents/skills" ;;
32
- goose) echo ".config/goose/skills .agents/skills" ;;
33
- amp) echo ".config/agents/skills .agents/skills" ;;
34
- opencode) echo "__OPENCODE__" ;;
35
- windsurf) echo ".codeium/windsurf/skills" ;;
36
- workbuddy) echo ".workbuddy/skills" ;;
37
- kiro) echo ".kiro/skills" ;;
38
- trae) echo ".traecli/skills" ;;
39
- trae-cn) echo ".trae-cn/skills" ;;
40
- qwen) echo ".qwen/skills" ;;
41
- comate) echo ".comate/skills" ;;
42
- codebuddy) echo ".codebuddy/skills" ;;
43
- kimi) echo ".kimi/skills" ;;
44
- agents) echo ".agents/skills" ;;
45
- *) return 1 ;;
46
- esac
47
- }
48
-
49
- codex_dir() {
50
- if [ -n "${CODEX_HOME:-}" ]; then printf '%s' "$CODEX_HOME/skills"; else printf '%s' "$HOME/.codex/skills"; fi
51
- }
52
- opencode_dir() {
53
- if [ -n "${XDG_CONFIG_HOME:-}" ]; then printf '%s' "$XDG_CONFIG_HOME/opencode/skills"; else printf '%s' "$HOME/.config/opencode/skills"; fi
54
- }
55
- resolve_user() {
56
- case "$1" in
57
- __CODEX__) codex_dir ;;
58
- __OPENCODE__) opencode_dir ;;
59
- *) printf '%s' "$HOME/$1" ;;
60
- esac
61
- }
62
-
63
- install_to() {
64
- mkdir -p "$1/$SKILL_NAME"
65
- cp -r "$SOURCE_DIR/." "$1/$SKILL_NAME/"
66
- rm -rf "$1/$SKILL_NAME/.git"
67
- echo "installed -> $1/$SKILL_NAME"
68
- }
69
-
70
- list() {
71
- echo "智能体 -> 默认技能目录(--agent <name> 装到第一个,用户级):"
72
- for a in claude cursor codex gemini goose amp opencode windsurf workbuddy kiro trae trae-cn qwen comate codebuddy kimi agents; do
73
- local dirs first
74
- dirs="$(dirs_for "$a")"
75
- first="${dirs%% *}"
76
- case "$first" in
77
- __CODEX__) first=".codex/skills" ;;
78
- __OPENCODE__) first=".config/opencode/skills" ;;
79
- esac
80
- if [ "$_IS_WINDOWS" = "1" ]; then
81
- first="%USERPROFILE%\\${first//\//\\}"
82
- else
83
- first="~/$first"
84
- fi
85
- printf ' %-10s %s\n' "$a" "$first"
86
- done
87
- echo '说明:Windows 用 %USERPROFILE%,Linux/macOS 用 ~;仅收录有官方默认目录的智能体。'
88
- echo '改了目录的请用 --dir <路径>,不要依赖默认位置;若设置了 CODEX_HOME / XDG_CONFIG_HOME,安装自动以该变量为准。'
89
- }
90
-
91
- main() {
92
- local agent="" dir="" global=0 show_list=0
93
- while [ $# -gt 0 ]; do
94
- case "$1" in
95
- --agent) shift; agent="${1:-}" ;;
96
- --dir) shift; dir="${1:-}" ;;
97
- -g|--global) global=1 ;;
98
- --list|-l) show_list=1 ;;
99
- *) echo "未知参数: $1" >&2; exit 2 ;;
100
- esac
101
- shift
102
- done
103
-
104
- if [ "$show_list" = "1" ]; then list; return; fi
105
- if [ -n "$dir" ]; then install_to "$dir"; echo "完成。"; return; fi
106
- if [ -n "$agent" ]; then
107
- local dirs first
108
- if ! dirs="$(dirs_for "$agent")"; then
109
- echo "未收录智能体: $agent。请用 --dir <路径> 指定技能目录。" >&2; exit 2
110
- fi
111
- first="${dirs%% *}"
112
- install_to "$(resolve_user "$first")"; echo "完成。"; return
113
- fi
114
- if [ "$global" = "1" ]; then
115
- echo "安装到全部已知智能体用户级目录..."
116
- local dirs rel
117
- for a in claude cursor codex gemini goose amp opencode windsurf workbuddy kiro trae trae-cn qwen comate codebuddy kimi agents; do
118
- dirs="$(dirs_for "$a")"
119
- for rel in $dirs; do install_to "$(resolve_user "$rel")"; done
120
- done
121
- echo "完成。"; return
122
- fi
123
- local installed=0 d
124
- for d in .claude/skills .cursor/skills .codex/skills .config/goose/skills .config/agents/skills .opencode/skills .codeium/windsurf/skills .workbuddy/skills .kiro/skills .traecli/skills .gemini/skills .trae-cn/skills .qwen/skills .comate/skills .codebuddy/skills .kimi/skills .agents/skills; do
125
- if [ -d "$d" ]; then install_to "$d"; installed=1; fi
126
- done
127
- if [ "$installed" = "0" ]; then
128
- echo "未检测到项目级智能体目录。可用 --agent <name> / -g 装到用户级,或 --dir 指定。"
129
- fi
130
- }
131
-
1
+ #!/usr/bin/env bash
2
+ # yotta-humanize 多智能体安装脚本(YottaSkills 模板)
3
+ # 用法:
4
+ # bash install.sh --agent <name> # 按智能体默认用户级目录安装
5
+ # bash install.sh --dir <path> # 装到指定目录(用户改过目录的智能体)
6
+ # bash install.sh -g # 装到全部已知智能体用户级目录
7
+ # bash install.sh # 检测并安装到已存在的项目级目录
8
+ # bash install.sh --list # 列出智能体 -> 默认目录
9
+ set -euo pipefail
10
+
11
+ SKILL_NAME="yotta-humanize"
12
+ SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
+ case "$(uname -s)" in
14
+ MINGW*|MSYS*)
15
+ SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -W)"
16
+ ;;
17
+ esac
18
+
19
+ # 智能体 -> 用户级默认目录(--agent 装到第一个)
20
+ # .agents/skills 并非通用目录:OpenCode / Cursor / Cline / Amp / Kimi / Gemini CLI / GitHub Copilot 读取。
21
+ # 判断当前环境:Windows Git Bash 用 %USERPROFILE%,Unix 用 ~
22
+ _IS_WINDOWS=0
23
+ case "$(uname -s)" in
24
+ MINGW*|MSYS*|CYGWIN*) _IS_WINDOWS=1 ;;
25
+ esac
26
+ dirs_for() {
27
+ case "$1" in
28
+ claude) echo ".claude/skills" ;;
29
+ cursor) echo ".cursor/skills .agents/skills" ;;
30
+ codex) echo "__CODEX__" ;;
31
+ gemini) echo ".gemini/skills .agents/skills" ;;
32
+ goose) echo ".config/goose/skills .agents/skills" ;;
33
+ amp) echo ".config/agents/skills .agents/skills" ;;
34
+ opencode) echo "__OPENCODE__" ;;
35
+ windsurf) echo ".codeium/windsurf/skills" ;;
36
+ workbuddy) echo ".workbuddy/skills" ;;
37
+ kiro) echo ".kiro/skills" ;;
38
+ trae) echo ".traecli/skills" ;;
39
+ trae-cn) echo ".trae-cn/skills" ;;
40
+ qwen) echo ".qwen/skills" ;;
41
+ comate) echo ".comate/skills" ;;
42
+ codebuddy) echo ".codebuddy/skills" ;;
43
+ kimi) echo ".kimi/skills" ;;
44
+ agents) echo ".agents/skills" ;;
45
+ *) return 1 ;;
46
+ esac
47
+ }
48
+
49
+ codex_dir() {
50
+ if [ -n "${CODEX_HOME:-}" ]; then printf '%s' "$CODEX_HOME/skills"; else printf '%s' "$HOME/.codex/skills"; fi
51
+ }
52
+ opencode_dir() {
53
+ if [ -n "${XDG_CONFIG_HOME:-}" ]; then printf '%s' "$XDG_CONFIG_HOME/opencode/skills"; else printf '%s' "$HOME/.config/opencode/skills"; fi
54
+ }
55
+ resolve_user() {
56
+ case "$1" in
57
+ __CODEX__) codex_dir ;;
58
+ __OPENCODE__) opencode_dir ;;
59
+ *) printf '%s' "$HOME/$1" ;;
60
+ esac
61
+ }
62
+
63
+ install_to() {
64
+ mkdir -p "$1/$SKILL_NAME"
65
+ cp -r "$SOURCE_DIR/." "$1/$SKILL_NAME/"
66
+ rm -rf "$1/$SKILL_NAME/.git"
67
+ echo "installed -> $1/$SKILL_NAME"
68
+ }
69
+
70
+ list() {
71
+ echo "智能体 -> 默认技能目录(--agent <name> 装到第一个,用户级):"
72
+ for a in claude cursor codex gemini goose amp opencode windsurf workbuddy kiro trae trae-cn qwen comate codebuddy kimi agents; do
73
+ local dirs first
74
+ dirs="$(dirs_for "$a")"
75
+ first="${dirs%% *}"
76
+ case "$first" in
77
+ __CODEX__) first=".codex/skills" ;;
78
+ __OPENCODE__) first=".config/opencode/skills" ;;
79
+ esac
80
+ if [ "$_IS_WINDOWS" = "1" ]; then
81
+ first="%USERPROFILE%\\${first//\//\\}"
82
+ else
83
+ first="~/$first"
84
+ fi
85
+ printf ' %-10s %s\n' "$a" "$first"
86
+ done
87
+ echo '说明:Windows 用 %USERPROFILE%,Linux/macOS 用 ~;仅收录有官方默认目录的智能体。'
88
+ echo '改了目录的请用 --dir <路径>,不要依赖默认位置;若设置了 CODEX_HOME / XDG_CONFIG_HOME,安装自动以该变量为准。'
89
+ }
90
+
91
+ main() {
92
+ local agent="" dir="" global=0 show_list=0
93
+ while [ $# -gt 0 ]; do
94
+ case "$1" in
95
+ --agent) shift; agent="${1:-}" ;;
96
+ --dir) shift; dir="${1:-}" ;;
97
+ -g|--global) global=1 ;;
98
+ --list|-l) show_list=1 ;;
99
+ *) echo "未知参数: $1" >&2; exit 2 ;;
100
+ esac
101
+ shift
102
+ done
103
+
104
+ if [ "$show_list" = "1" ]; then list; return; fi
105
+ if [ -n "$dir" ]; then install_to "$dir"; echo "完成。"; return; fi
106
+ if [ -n "$agent" ]; then
107
+ local dirs first
108
+ if ! dirs="$(dirs_for "$agent")"; then
109
+ echo "未收录智能体: $agent。请用 --dir <路径> 指定技能目录。" >&2; exit 2
110
+ fi
111
+ first="${dirs%% *}"
112
+ install_to "$(resolve_user "$first")"; echo "完成。"; return
113
+ fi
114
+ if [ "$global" = "1" ]; then
115
+ echo "安装到全部已知智能体用户级目录..."
116
+ local dirs rel
117
+ for a in claude cursor codex gemini goose amp opencode windsurf workbuddy kiro trae trae-cn qwen comate codebuddy kimi agents; do
118
+ dirs="$(dirs_for "$a")"
119
+ for rel in $dirs; do install_to "$(resolve_user "$rel")"; done
120
+ done
121
+ echo "完成。"; return
122
+ fi
123
+ local installed=0 d
124
+ for d in .claude/skills .cursor/skills .codex/skills .config/goose/skills .config/agents/skills .opencode/skills .codeium/windsurf/skills .workbuddy/skills .kiro/skills .traecli/skills .gemini/skills .trae-cn/skills .qwen/skills .comate/skills .codebuddy/skills .kimi/skills .agents/skills; do
125
+ if [ -d "$d" ]; then install_to "$d"; installed=1; fi
126
+ done
127
+ if [ "$installed" = "0" ]; then
128
+ echo "未检测到项目级智能体目录。可用 --agent <name> / -g 装到用户级,或 --dir 指定。"
129
+ fi
130
+ }
131
+
132
132
  main "$@"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yottameta/yotta-humanize",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Yuanzhen (元真) — AI-flavor remover for Chinese writing: a detector engine (24 rule classes + wordlists + statistical burstiness) that identifies and rewrites AI-typical Chinese text to make it read more naturally. Triggers when editing/polishing text, removing AI flavor, or making copy read more human. Boundaries: text-only, deterministic rewrites, no model dependency.",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -18,7 +18,10 @@
18
18
  "bin",
19
19
  "NOTICE",
20
20
  "CHANGELOG.md",
21
- "README.zh-CN.md"
21
+ "README.zh-CN.md",
22
+ "!scripts/__pycache__",
23
+ "!**/__pycache__",
24
+ "!**/*.pyc"
22
25
  ],
23
26
  "repository": {
24
27
  "type": "git",
@@ -29,5 +32,8 @@
29
32
  },
30
33
  "bin": {
31
34
  "yotta-humanize": "bin/install.js"
35
+ },
36
+ "scripts": {
37
+ "test": "node --test \"test/*.test.js\" && python scripts/test_yotta_humanize.py"
32
38
  }
33
39
  }
@@ -0,0 +1,51 @@
1
+ # 元真 FAQ / 避坑指南
2
+
3
+ > 常见问题速查:文本传不进去、结果不像预期、文件报错时先看这里。
4
+
5
+ ## 速查索引
6
+
7
+ - **输入**:怎么传文本 · UTF-8 报错 · 长文怎么处理
8
+ - **结果**:改写变化不大 · rewrite/suggest 区别 · 会不会改坏
9
+ - **规则**:检测原理 · CI 拦截 · 退出码
10
+ - **使用**:什么时候自动触发 · 安装/智能体目录
11
+
12
+ ## 1. 怎么把文本传进去?
13
+
14
+ ## 1. 怎么把文本传进去?
15
+ 三种方式任选:`-f 文件.txt`(读 UTF-8 文件)、`--stdin`(管道输入)、直接传文本参数(`yotta-humanize score "这段文字"`)。注意顺序:`score / analyze / report / suggest / rewrite` 子命令 + 输入方式。
16
+
17
+ ## 2. 报「不是 UTF-8 编码」?
18
+ 把文件另存为 UTF-8 编码再试(记事本 → 另存为 → 编码选 UTF-8)。Windows 的 GBK/ANSI 文件需先转码。
19
+
20
+ ## 3. 文件太大 / 长文处理?
21
+ 建议分段处理:每段单独 `rewrite` 或 `suggest`,再拼接。复杂长文中的嵌套问题可能需要人工复核,元真只做确定性规则改写,不保证处理所有嵌套句式。
22
+
23
+ ## 4. 改写后变化不大?
24
+ 说明原文 AI 味本身不高(`score` 低)。先跑 `score` 看评分,再 `analyze` 看命中的规则;确需润色时用 `suggest` 获取人工润色建议,或 `rewrite` 做机械改写。
25
+
26
+ ## 5. rewrite 和 suggest 有什么区别?
27
+ `rewrite`:确定性机械改写(命中规则就替换/删除),输出改写后全文 + 修复清单 + 前后评分。
28
+ `suggest`:按优先级给改写建议(不直接改原文),适合人工精修。
29
+
30
+ ## 6. CI 拦截怎么用?
31
+ `yotta-humanize rewrite --gate --threshold 40`:改写后评分仍 >= 阈值时退出码 1,可在 CI 里拦截 AI 腔内容。`--gate` 需搭配 `score` 或 `rewrite`。
32
+
33
+ ## 7. 退出码 0 / 1 / 4 什么意思?
34
+ 0 = 成功;1 = `--gate` 命中(评分 >= 阈值,用于 CI 拦截);4 = 输入错误(文件不存在、无输入等)。
35
+
36
+ ## 8. 会不会改坏原文?
37
+ `rewrite` 只做确定性替换/删除(规则命中才动),输出带修复清单(改了什么、改了几处)和 before/after 评分,可逐条核对。不改写事实、数据、专有名词。
38
+
39
+ ## 9. 检测原理是什么?
40
+ 24 类规则 + 词表(赋能、闭环、值得注意的是、综上所述、希望对你有所帮助 等 AI 常用词与句式)+ 统计突发性,全部确定性规则,不依赖任何模型,结果可复现。
41
+
42
+ ## 10. 边界
43
+ 只处理文本,不生成新内容;不改写事实 / 数据 / 专有名词;不破坏作者原意。AI 腔极重或嵌套复杂的文本,建议配合人工润色。
44
+
45
+ ## 11. 什么时候会触发?
46
+
47
+ 当你编辑/润色文本、给 AI 生成内容做检测、或发现文本里满是“赋能 / 闭环 / 综上所述 / 希望对你有所帮助”这类 AI 常用词时触发;用户也可以直接说“元真 / 去 AI 味 / 帮我改得像人写的”。
48
+
49
+ ## 12. 安装不上 / 智能体没识别怎么办?
50
+
51
+ 用 `npx -y @yottameta/yotta-humanize --agent <名称>` 装到已知智能体,或 `--dir <技能目录>` 装到自定义路径;未收录智能体用 `--dir`。走国内 npm 镜像遇到传播延迟时临时加 `--registry=https://registry.npmjs.org/`。装完后新会话自动加载才会生效。
@@ -0,0 +1,28 @@
1
+ # 元真复杂场景走查
2
+
3
+ ## 走查 1:公众号推文去 AI 味
4
+
5
+ 输入:一篇约 2000 字的“行业观察”,开头“在当今数字化浪潮的背景下……赋能……闭环……总而言之”,结尾“希望对你有所帮助”。
6
+
7
+ 1. 先 `score -f article.md` 拿基线分;预期偏高。
8
+ 2. `analyze` 看命中规则,优先处理套话开头/结尾、赋能/闭环等词表替换。
9
+ 3. `rewrite` 做机械改写,核对修复清单;保留事实数据与专有名词。
10
+ 4. 对判断性改写(排比拆句、模糊归因)用 `suggest` 输出人工建议,不在 rewrite 里强改。
11
+ 5. 二次 `score` 对比,确认评分下降且原意未变。
12
+
13
+ ## 走查 2:会议纪要不破坏正式语气
14
+
15
+ 输入:一段会议纪要,AI 痕迹主要是“值得注意的是”“综上所述”“我们”滥用,但语气偏正式。
16
+
17
+ 1. 先跑 `analyze` 分五组看命中,避免只按词表删。
18
+ 2. 正式文档优先 `suggest` 而不是 `rewrite`,让作者选择保留正式语气的改写。
19
+ 3. 检查“我们”是否真的冗余;数据、结论、待办一字不改。
20
+ 4. 输出改写建议清单供人工确认,而不是直接交付改写稿。
21
+
22
+ ## 走查 3:CSV/接口文案批量门禁
23
+
24
+ 输入:一批由 AI 生成的中文短文案,要统一过 CI 防 AI 味。
25
+
26
+ 1. 用 `score --stdin --gate --threshold 45` 接入发布前检查。
27
+ 2. 超阈值的条目单独 `suggest`/`rewrite`,保留作者原意。
28
+ 3. 门禁只看确定性评分,不替代人工编辑;评分低于阈值只代表未命中规则,不代表“一定不像 AI 写的”。
@@ -47,7 +47,7 @@ _HERE = Path(__file__).resolve().parent
47
47
  sys.path.insert(0, str(_HERE))
48
48
  import humanize_rules as HR # noqa: E402
49
49
 
50
- VERSION = "0.1.2"
50
+ VERSION = "0.2.0"
51
51
  TOOL_NAME = "yotta-humanize"
52
52
  TOOL_CN = "元真"
53
53
  DEFAULT_THRESHOLD = 45
@@ -515,8 +515,13 @@ def _read_text(args):
515
515
  if getattr(args, "file", None):
516
516
  p = Path(args.file)
517
517
  if not p.exists():
518
- raise SystemExit("文件不存在:%s" % p)
519
- return p.read_text(encoding="utf-8")
518
+ raise SystemExit("文件不存在:%s\n修复建议:请检查文件路径是否正确、文件名是否拼错。" % p)
519
+ try:
520
+ return p.read_text(encoding="utf-8")
521
+ except UnicodeDecodeError:
522
+ raise SystemExit("无法读取文件:%s 不是 UTF-8 编码\n修复建议:请把文件另存为 UTF-8 编码后重试(记事本 → 另存为 → 编码选 UTF-8)。" % p)
523
+ except OSError as e:
524
+ raise SystemExit("无法读取文件:%s(%s)\n修复建议:请检查文件是否存在、是否有读取权限。" % (p, e))
520
525
  if getattr(args, "stdin", False):
521
526
  return _read_stdin()
522
527
  if getattr(args, "text", None):
@@ -621,7 +626,7 @@ def main(argv=None):
621
626
  print(res["text"])
622
627
  return 0
623
628
  except Exception as e: # noqa: BLE001
624
- print("错误:%s" % e, file=sys.stderr)
629
+ print("错误:%s\n修复建议:请检查输入是否为 UTF-8 编码、文件是否存在;若仍报错,请把上面的错误信息反馈给开发者。" % e, file=sys.stderr)
625
630
  return 4
626
631
  return 4
627
632