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 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` | 全面诊断 21 项(Python/依赖/版本一致性/字体/DSH 文件/代码风格),自动修复常见问题 |
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.2.0
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.2.0"
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.2.0+ |
451
+ | DSH 配置化排版参数(页边距/行距/字体/默认模板版本) | ✅ v2.3.0+ |
452
452
 
453
- ### DSH 插件配置化(v2.2.0+)
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.2.0(多渠道自检已确认最新)
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.2.0+)
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,21 项自检)、自动修复(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 系统设置→插件配置管理。`;
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";
@@ -7,7 +7,7 @@
7
7
  #
8
8
  # 公文全流程处理工具 - gongwen-skill Python package
9
9
 
10
- __version__ = "2.2.0"
10
+ __version__ = "2.3.0"
11
11
 
12
12
  # Re-export everything from the legacy module for backward compatibility
13
13
  # This allows: from gongwen import main, cmd_check, etc.
@@ -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.2.0"
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
- dsh_skill = _PROJECT_ROOT / ".dsh" / "skills" / "gongwen-skill.md"
169
- dsh_skill2 = _PROJECT_ROOT / ".dsh" / "skills" / "gongwen-skill" / "SKILL.md"
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" / "gongwen-skill.md",
567
- _PROJECT_ROOT / ".dsh" / "skills" / "gongwen-skill" / "SKILL.md",
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gongwen-skill",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "公文全流程处理工具 - GB/T 9704 格式检查/修复/内容优化/模板生成/版式注入",
5
5
  "type": "module",
6
6
  "main": "dsh/index.js",
@@ -201,7 +201,7 @@ python -m gongwen optimize 文件.docx -o 优化版.docx
201
201
  **Agent 响应**:
202
202
  ```
203
203
  📋 合规自检报告
204
- Skill 版本: v2.2.0
204
+ Skill 版本: v2.3.0
205
205
  路径判定: B(内容优化)
206
206
  依据: 用户提供纯文本+优化要求,无已有文档
207
207
  命令调用: 将原文保存为临时文件后执行 optimize-content --apply --paragraphs "1-3"
package/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gongwen-skill"
7
- version = "2.2.0"
7
+ version = "2.3.0"
8
8
  description = "公文全流程处理工具 - GB/T 9704 格式检查/修复/内容优化/模板生成/版式注入"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}