gongwen-skill 2.2.0 → 2.3.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 +12 -0
- package/README.md +6 -6
- package/dsh/index.js +2 -2
- package/gongwen/__init__.py +1 -1
- package/gongwen/_legacy.py +1 -1
- package/gongwen/cli/doctor_cmds.py +157 -4
- package/package.json +1 -1
- package/prompts/usage-prompts.md +1 -1
- package/pyproject.toml +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@
|
|
|
4
4
|
Licensed under the MIT License. See the LICENSE file for details.
|
|
5
5
|
-->
|
|
6
6
|
|
|
7
|
+
## v2.3.0 (2026-08-31)
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **doctor 自检新增「DSH 技能 frontmatter」检查项**(21 项 → 22 项):按 DSH 最新技能规范校验 SKILL.md frontmatter——`name` 必填且为 kebab-case、与 `.dsh/skills/<name>` 目录/文件名一致;`description` 必填非空且足够自描述(模型在会话目录只能看到它);`whenToUse` 建议存在;`user-invocable`/`disable-model-invocation` 若存在必须为布尔值;frontmatter 必须 `---` 包裹且 YAML 可解析
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- **技能名动态化**:新增 `_get_skill_name()` 从 SKILL.md frontmatter 读取技能名,`_check_skill_sync`/`cmd_repair` 不再硬编码 `gongwen-skill`,技能改名后检查与修复仍准确
|
|
14
|
+
- **纯 pip 环境不再误报**:frontmatter 目录一致性校验仅在 `.dsh/skills` 存在时执行(非 DSH 环境跳过)
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- **SKILL.md 编码读取修复**:读取编码 `utf-8` → `utf-8-sig`(自动剥离 BOM,兼容带/无 BOM);修复 Windows 下默认 locale 编码(cp936/GBK)读 UTF-8 无 BOM 的 SKILL.md 抛 `UnicodeDecodeError` 的隐患
|
|
18
|
+
|
|
7
19
|
## v2.2.0 (2026-08-28)
|
|
8
20
|
|
|
9
21
|
### Added
|
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ Licensed under the MIT License. See the LICENSE file for details.
|
|
|
46
46
|
| 🧩 完整审校 | `full-review` | 修订+批注联合命令(句子级差异修订 + 分类批注) |
|
|
47
47
|
| 🎨 样式学习 | `style-learn` / `style-list` | 上传标准文档学习 Run/段落/页面三级样式(字体/字号/字间距/行距/缩进/页边距),生成命名模板持久化,后续用 `optimize -t 模板名` 套用 |
|
|
48
48
|
| 🔄 版本自检 | `check-update` | 多渠道版本自检(GitHub/GitCode/AtomGit 三仓库比对取最新) |
|
|
49
|
-
| 🩺 自我诊断 | `doctor` / `repair` | 全面诊断
|
|
49
|
+
| 🩺 自我诊断 | `doctor` / `repair` | 全面诊断 22 项(Python/依赖/版本一致性/字体/DSH 文件/DSH 技能 frontmatter/代码风格),自动修复常见问题 |
|
|
50
50
|
| 🕵️ 文档审计 | `audit` | 检查删除线/加粗/AI 声明等痕迹 |
|
|
51
51
|
| 🤝 会话交接 | `handoff` | 跨会话上下文传递(`--list` / `--latest` / Agent 长任务收尾必写) |
|
|
52
52
|
| ⚙️ 规则管理 | `rule-export/import/list` | YAML 规则三层定制(官方/单位/用户) |
|
|
@@ -337,7 +337,7 @@ DSH 采用 **Cordis 模块化微内核架构**:技能体系基于本地文件
|
|
|
337
337
|
git clone https://github.com/linhut/gongwen-skill.git
|
|
338
338
|
cd gongwen-skill
|
|
339
339
|
pip install -r requirements.txt # 或 pip install gongwen-skill(已上 PyPI)
|
|
340
|
-
python -m gongwen --version # 检验:gongwen-skill v2.
|
|
340
|
+
python -m gongwen --version # 检验:gongwen-skill v2.3.0
|
|
341
341
|
```
|
|
342
342
|
|
|
343
343
|
### 方式一:作为 DSH Skill 注册(基于本地文件系统)
|
|
@@ -393,7 +393,7 @@ pnpm add -w gongwen-skill
|
|
|
393
393
|
"dependencies": {
|
|
394
394
|
"@deepseek-ai/dsh-base": "...",
|
|
395
395
|
"@deepseek-ai/dsh-web-app": "...",
|
|
396
|
-
"gongwen-skill": "^2.
|
|
396
|
+
"gongwen-skill": "^2.3.0"
|
|
397
397
|
},
|
|
398
398
|
"dsh": {
|
|
399
399
|
"profile": {
|
|
@@ -448,9 +448,9 @@ dsh --profile web
|
|
|
448
448
|
| CLI 独立可执行(`python -m gongwen <命令>`) | ✅ |
|
|
449
449
|
| PyPI 上架(`pip install gongwen-skill`) | ✅ |
|
|
450
450
|
| 零外部运行时依赖(仅 python-docx/pydantic/pyyaml) | ✅ |
|
|
451
|
-
| DSH 配置化排版参数(页边距/行距/字体/默认模板版本) | ✅ v2.
|
|
451
|
+
| DSH 配置化排版参数(页边距/行距/字体/默认模板版本) | ✅ v2.3.0+ |
|
|
452
452
|
|
|
453
|
-
### DSH 插件配置化(v2.
|
|
453
|
+
### DSH 插件配置化(v2.3.0+)
|
|
454
454
|
|
|
455
455
|
DSH 插件支持通过配置文件管理排版参数,Agent 调用时自动注入,纯 CLI 用户不受影响。
|
|
456
456
|
|
|
@@ -574,7 +574,7 @@ pip install -r requirements.txt
|
|
|
574
574
|
用户:帮我优化这份会议通知的第二章节措辞
|
|
575
575
|
|
|
576
576
|
Agent:📋 合规自检报告
|
|
577
|
-
Skill 版本: v2.
|
|
577
|
+
Skill 版本: v2.3.0(多渠道自检已确认最新)
|
|
578
578
|
路径判定: B(内容优化)
|
|
579
579
|
依据: 用户指定了已有文档,且要求"优化措辞"
|
|
580
580
|
命令调用: 1. python -m gongwen optimize-content 会议通知.docx --changes changes.json --apply --paragraphs "5-8"
|
package/dsh/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// 公文全流程处理工具 - DSH plugin bridge (gongwen-skill, v2.
|
|
1
|
+
// 公文全流程处理工具 - DSH plugin bridge (gongwen-skill, v2.3.0+)
|
|
2
2
|
// (c) 2026 Jose AI (https://www.linhut.cn)
|
|
3
3
|
// https://github.com/linhut/gongwen-skill
|
|
4
4
|
// Licensed under the MIT License. See the LICENSE file for details.
|
|
@@ -30,7 +30,7 @@ const CONFIG_FILE = join(APP_DATA_DIR, "dsh-config.json");
|
|
|
30
30
|
const DEFAULTS_FILE = join(resolve(__dirname, ".."), "etc", "dsh-config-defaults.json");
|
|
31
31
|
|
|
32
32
|
// AI 工作指引
|
|
33
|
-
const GONGWEN_GUIDANCE = `本机已安装公文全流程处理工具插件(gongwen-skill)。能力:.docx 公文全流程——列出公文类型(list-types)、解析文档(parse)、格式检查(check)、自动修复(optimize)、内容修订对比版(optimize-content)、模板生成(template)、样式学习(style-learn/style-list,从标准文档学习排版样式)、全面诊断(doctor,
|
|
33
|
+
const GONGWEN_GUIDANCE = `本机已安装公文全流程处理工具插件(gongwen-skill)。能力:.docx 公文全流程——列出公文类型(list-types)、解析文档(parse)、格式检查(check)、自动修复(optimize)、内容修订对比版(optimize-content)、模板生成(template)、样式学习(style-learn/style-list,从标准文档学习排版样式)、全面诊断(doctor,22 项自检)、自动修复(repair)、Markdown 转公文(md2docx)、JSON 模型生成(generate)、版头/版记/页码注入(header/footer/pagenum)、首句加粗(bold-first)、一键格式修复(fix-common)、桌签生成(table-signs)、审稿流转单(review)、完整审校(full-review)、文档审计(audit)、规则管理(rule-export/import/list)、版本自检(check-update)、会话交接(handoff)、字体管理(font)。覆盖通知/请示/报告/函/会议纪要等 24 类公文。完全自包含,克隆即用,无需数据库或后端服务。用户提到「公文 / 红头文件 / 版式 / 排版 / 格式检查 / 公文模板 / 样式学习 / 自定义模板 / 党政机关公文」时即指本插件。DSH 插件支持配置化排版参数(页边距/行距/字体等),配置文件位于 ~/.gongwen-skill/dsh-config.json,可通过 config 命令或 DSH 系统设置→插件配置管理。`;
|
|
34
34
|
|
|
35
35
|
// Web API 路由前缀
|
|
36
36
|
const API_PREFIX = "/plugins/gongwen-skill/api";
|
package/gongwen/__init__.py
CHANGED
package/gongwen/_legacy.py
CHANGED
|
@@ -45,7 +45,7 @@ from gongwen.cli.helpers import (
|
|
|
45
45
|
parse_config_overrides as _parse_config_overrides,
|
|
46
46
|
load_rules_with_overrides as _load_rules_with_overrides,
|
|
47
47
|
)
|
|
48
|
-
__version__ = "2.
|
|
48
|
+
__version__ = "2.3.0"
|
|
49
49
|
# 版本号应与 gongwen/__init__.py 保持一致,每次发版同步更新
|
|
50
50
|
"""
|
|
51
51
|
中文公文全流程处理工具 —— 基于 GB/T 9704《党政机关公文格式》国家标准。
|
|
@@ -162,11 +162,45 @@ def _check_fonts() -> dict:
|
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
|
|
165
|
+
def _get_skill_name() -> str:
|
|
166
|
+
"""从 SKILL.md frontmatter 读取技能名(DSH 规范:name 即目录名)。
|
|
167
|
+
|
|
168
|
+
供 _check_skill_sync / _check_skill_frontmatter / cmd_repair 共用,
|
|
169
|
+
避免硬编码技能名导致改名后检查失准。无法解析时返回空串。
|
|
170
|
+
"""
|
|
171
|
+
skill_path = _PROJECT_ROOT / "SKILL.md"
|
|
172
|
+
try:
|
|
173
|
+
# P2-29:utf-8-sig 自动剥离 BOM,兼容带 BOM/无 BOM 的 UTF-8;
|
|
174
|
+
# 若用默认编码(Windows 下 cp936/GBK)读 UTF-8 无 BOM 文件会抛 UnicodeDecodeError
|
|
175
|
+
text = skill_path.read_text(encoding="utf-8-sig")
|
|
176
|
+
if not text.startswith("---"):
|
|
177
|
+
return ""
|
|
178
|
+
end = text.index("\n---", 4)
|
|
179
|
+
fm_text = text[4:end]
|
|
180
|
+
except Exception:
|
|
181
|
+
return ""
|
|
182
|
+
try:
|
|
183
|
+
import yaml
|
|
184
|
+
fm = yaml.safe_load(fm_text) or {}
|
|
185
|
+
name = fm.get("name")
|
|
186
|
+
return str(name).strip() if name else ""
|
|
187
|
+
except Exception:
|
|
188
|
+
# 回退:简单键值解析
|
|
189
|
+
for line in fm_text.splitlines():
|
|
190
|
+
line = line.strip()
|
|
191
|
+
if line.startswith("name:"):
|
|
192
|
+
return line.split(":", 1)[1].strip()
|
|
193
|
+
return ""
|
|
194
|
+
|
|
195
|
+
|
|
165
196
|
def _check_skill_sync() -> dict:
|
|
166
197
|
"""检查 .dsh/skills/ 中的 SKILL.md 副本是否与根目录一致。"""
|
|
167
198
|
root_skill = _PROJECT_ROOT / "SKILL.md"
|
|
168
|
-
|
|
169
|
-
|
|
199
|
+
# P2-26:技能名从 frontmatter 动态读取(DSH 规范 name 即目录名),
|
|
200
|
+
# 避免硬编码 gongwen-skill 导致改名后同步检查失准;SKILL.md 缺失时回退默认名
|
|
201
|
+
skill_name = _get_skill_name() or "gongwen-skill"
|
|
202
|
+
dsh_skill = _PROJECT_ROOT / ".dsh" / "skills" / f"{skill_name}.md"
|
|
203
|
+
dsh_skill2 = _PROJECT_ROOT / ".dsh" / "skills" / skill_name / "SKILL.md"
|
|
170
204
|
|
|
171
205
|
if not root_skill.exists():
|
|
172
206
|
return {
|
|
@@ -205,6 +239,122 @@ def _check_skill_sync() -> dict:
|
|
|
205
239
|
}
|
|
206
240
|
|
|
207
241
|
|
|
242
|
+
def _check_skill_frontmatter() -> dict:
|
|
243
|
+
"""(P2-25)检查 SKILL.md frontmatter 是否符合 DSH 技能规范。
|
|
244
|
+
|
|
245
|
+
依据 DSH 最新技能编写规范(writing-skills):
|
|
246
|
+
- name 必须存在、为 kebab-case、且与技能目录名一致
|
|
247
|
+
- description 必须存在、非空,是模型在会话目录中看到的唯一自描述
|
|
248
|
+
- whenToUse 建议存在(触发条件,便于模型匹配)
|
|
249
|
+
- user-invocable / disable-model-invocation 若存在必须为布尔值
|
|
250
|
+
frontmatter 必须以 --- 包裹且可被 YAML 解析。
|
|
251
|
+
"""
|
|
252
|
+
import re as _re
|
|
253
|
+
|
|
254
|
+
skill_path = _PROJECT_ROOT / "SKILL.md"
|
|
255
|
+
if not skill_path.exists():
|
|
256
|
+
return {
|
|
257
|
+
"name": "DSH 技能 frontmatter",
|
|
258
|
+
"ok": False,
|
|
259
|
+
"detail": "根目录 SKILL.md 不存在",
|
|
260
|
+
"hint": "项目文件不完整",
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
# P2-29:utf-8-sig 自动剥离 BOM,兼容带 BOM/无 BOM 的 UTF-8;
|
|
264
|
+
# 若用默认编码(Windows 下 cp936/GBK)读 UTF-8 无 BOM 文件会抛 UnicodeDecodeError
|
|
265
|
+
text = skill_path.read_text(encoding="utf-8-sig")
|
|
266
|
+
if not text.startswith("---"):
|
|
267
|
+
return {
|
|
268
|
+
"name": "DSH 技能 frontmatter",
|
|
269
|
+
"ok": False,
|
|
270
|
+
"detail": "缺少 frontmatter(内容未以 --- 开头)",
|
|
271
|
+
"hint": "为 SKILL.md 添加 --- 包裹的 frontmatter",
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
# 解析 frontmatter(用 pyyaml 若可用,否则回退简单键值解析)
|
|
275
|
+
fm = None
|
|
276
|
+
try:
|
|
277
|
+
end = text.index("\n---", 4)
|
|
278
|
+
fm_text = text[4:end]
|
|
279
|
+
except ValueError:
|
|
280
|
+
fm_text = None
|
|
281
|
+
|
|
282
|
+
parse_ok = False
|
|
283
|
+
error_detail = ""
|
|
284
|
+
if fm_text is not None:
|
|
285
|
+
try:
|
|
286
|
+
import yaml
|
|
287
|
+
fm = yaml.safe_load(fm_text) or {}
|
|
288
|
+
parse_ok = isinstance(fm, dict)
|
|
289
|
+
except Exception as e:
|
|
290
|
+
error_detail = f"{str(e)[:80]}"
|
|
291
|
+
# 回退:简单键值解析
|
|
292
|
+
kv = {}
|
|
293
|
+
for line in fm_text.splitlines():
|
|
294
|
+
line = line.strip()
|
|
295
|
+
if line and ":" in line and not line.startswith("#"):
|
|
296
|
+
k, v = line.split(":", 1)
|
|
297
|
+
kv[k.strip()] = v.strip()
|
|
298
|
+
if kv:
|
|
299
|
+
fm = kv
|
|
300
|
+
parse_ok = True
|
|
301
|
+
|
|
302
|
+
if not parse_ok:
|
|
303
|
+
return {
|
|
304
|
+
"name": "DSH 技能 frontmatter",
|
|
305
|
+
"ok": False,
|
|
306
|
+
"detail": f"frontmatter 未找到或解析失败({error_detail or '缺少 --- 闭合'})",
|
|
307
|
+
"hint": "修正 SKILL.md 的 frontmatter YAML",
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
problems = []
|
|
311
|
+
|
|
312
|
+
# 1. name:存在、kebab-case
|
|
313
|
+
name = fm.get("name")
|
|
314
|
+
if not name:
|
|
315
|
+
problems.append("缺少 name 字段")
|
|
316
|
+
else:
|
|
317
|
+
if not _re.match(r"^[a-z0-9]+(-[a-z0-9]+)*$", str(name)):
|
|
318
|
+
problems.append(f"name '{name}' 不是 kebab-case")
|
|
319
|
+
# 与技能目录名一致(.dsh/skills/<name>/ 或 .dsh/skills/<name>.md)
|
|
320
|
+
# P2-27:仅当 .dsh/skills 目录存在时才校验,纯 pip 安装(非 DSH 环境)
|
|
321
|
+
# 不携带 .dsh/skills,不应误报目录缺失
|
|
322
|
+
dsh_skills = _PROJECT_ROOT / ".dsh" / "skills"
|
|
323
|
+
if dsh_skills.is_dir():
|
|
324
|
+
dir_candidates = [
|
|
325
|
+
str(dsh_skills / str(name)),
|
|
326
|
+
str(dsh_skills / f"{name}.md"),
|
|
327
|
+
]
|
|
328
|
+
if not any(Path(c).exists() for c in dir_candidates):
|
|
329
|
+
problems.append(f"name '{name}' 与 .dsh/skills 下无对应目录/文件")
|
|
330
|
+
|
|
331
|
+
# 2. description:存在、非空、自描述
|
|
332
|
+
desc = fm.get("description")
|
|
333
|
+
if not desc or not str(desc).strip():
|
|
334
|
+
problems.append("description 为空(模型只能看到 description,必须自描述)")
|
|
335
|
+
elif len(str(desc)) < 30:
|
|
336
|
+
problems.append(f"description 过短({len(str(desc))} 字),应完整自描述触发条件与能力")
|
|
337
|
+
|
|
338
|
+
# 3. whenToUse:建议存在
|
|
339
|
+
when = fm.get("whenToUse")
|
|
340
|
+
if not when or not str(when).strip():
|
|
341
|
+
problems.append("缺少 whenToUse(建议写明触发场景便于模型匹配)")
|
|
342
|
+
|
|
343
|
+
# 4. 可选布尔字段类型
|
|
344
|
+
for key in ("user-invocable", "disable-model-invocation"):
|
|
345
|
+
if key in fm and not isinstance(fm[key], bool):
|
|
346
|
+
problems.append(f"{key} 应为布尔值(true/false)")
|
|
347
|
+
|
|
348
|
+
ok = not problems
|
|
349
|
+
detail = f"name={fm.get('name', '?')}, description={len(str(fm.get('description', '')))} 字"
|
|
350
|
+
return {
|
|
351
|
+
"name": "DSH 技能 frontmatter",
|
|
352
|
+
"ok": ok,
|
|
353
|
+
"detail": detail if ok else "; ".join(problems),
|
|
354
|
+
"hint": None if ok else "对照 DSH 技能规范修正 SKILL.md frontmatter",
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
|
|
208
358
|
def _check_required_dirs() -> list[dict]:
|
|
209
359
|
"""检查必需目录是否存在。"""
|
|
210
360
|
results = []
|
|
@@ -430,6 +580,7 @@ def _run_all_checks() -> dict:
|
|
|
430
580
|
for file_result in _check_dsh_plugin_files():
|
|
431
581
|
add(file_result)
|
|
432
582
|
add(_check_skill_sync())
|
|
583
|
+
add(_check_skill_frontmatter())
|
|
433
584
|
add(_check_git_status())
|
|
434
585
|
add(_check_pycodestyle())
|
|
435
586
|
add(_check_npm_package())
|
|
@@ -562,9 +713,11 @@ def cmd_repair(args):
|
|
|
562
713
|
total += 1
|
|
563
714
|
print(f"[{total}] 同步 SKILL.md 到 .dsh/skills/...")
|
|
564
715
|
root_skill = _PROJECT_ROOT / "SKILL.md"
|
|
716
|
+
# P2-28:技能名从 frontmatter 动态读取,与 doctor 检查保持一致
|
|
717
|
+
repair_skill_name = _get_skill_name() or "gongwen-skill"
|
|
565
718
|
dsh_targets = [
|
|
566
|
-
_PROJECT_ROOT / ".dsh" / "skills" / "
|
|
567
|
-
_PROJECT_ROOT / ".dsh" / "skills" /
|
|
719
|
+
_PROJECT_ROOT / ".dsh" / "skills" / f"{repair_skill_name}.md",
|
|
720
|
+
_PROJECT_ROOT / ".dsh" / "skills" / repair_skill_name / "SKILL.md",
|
|
568
721
|
]
|
|
569
722
|
|
|
570
723
|
if not root_skill.exists():
|
package/package.json
CHANGED
package/prompts/usage-prompts.md
CHANGED