gongwen-skill 2.9.0 → 2.10.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,28 @@
4
4
  Licensed under the MIT License. See the LICENSE file for details.
5
5
  -->
6
6
 
7
+ ## v2.10.0 (2026-09-04)
8
+
9
+ ### Added
10
+ - **测试补强(整改 A / P1-1)**:新增 `tests/test_checker.py`/`test_optimize.py`/`test_content_cmds.py`/`test_json_output.py` 26 用例,覆盖 check/optimize/content 核心路径与 `--json` 输出(tests/ 本地-only,不同步仓库)
11
+ - **`parse`/`md2docx`/`rule-export` 统一 `--json`(整改 B / P1-2)**:Agent 高频命令补齐结构化输出,向后兼容(不带 `--json` 保持原行为)
12
+ - **CHK-L003 语义类规则静默跳过**:`language.*` 字段不再刷「未支持检查字段」告警(语言规范类由人工/LLM 审校覆盖,规则定义保留)
13
+ - **`optimize --json` 新增 `verify_executed`/`verify_passed` 明确字段(P2-3)**:`verify_passed = (verify_p0 == 0)` 与退出码语义一致,消除旧 `verified` 字段命名歧义(旧字段保留向后兼容)
14
+ - **README 新增 Windows 控制台编码说明(P2-5)**:原生 cmd GBK 乱码排查(chcp 65001 / Windows Terminal / PYTHONIOENCODING=utf-8)
15
+
16
+ ### Changed
17
+ - **`main()`/命令注册迁出 `_legacy.py`(整改 C / P0-1)**:29 个 add_parser 注册 + 命令分组/格式化器迁至 `gongwen/cli/app.py`;`_legacy.py` 1320→936 行,仅保留 10 个核心命令实现与兼容转发;入口 `from gongwen.cli.app import main`;迁移前后 29 命令 help/version 快照零差异
18
+
19
+ ### Fixed
20
+ - `tests/test_netcheck.py` 既有 F841(未使用变量)清理
21
+ - **8 处静默降级 pass 补日志(P2-4)**:md2docx_render 3 处 / draft_cmds 1 处 / wizard_cmds 1 处 / font_cmds 3 处,异常不再无声吞掉(新增 3 个模块 logger)
22
+ - **清理本地 dist/ 与 build/ 残留(P2-2)**:删除 2.7.0/2.8.0 旧构建产物(gitignore 忽略,不影响发布流程)
23
+
24
+ ### Notes
25
+ - 审计整改 D 决策(2026-09-04):remote token 保持现状(已查证未进入 git 历史,风险记录在本地审计报告);CI 接受现状(本地跑测试作发布门禁),详见 `docs/design/2026-09-04-project-audit-report.md` §6(本地存档)
26
+
27
+ ---
28
+
7
29
  ## v2.9.0 (2026-09-04)
8
30
 
9
31
  ### Added
package/README.md CHANGED
@@ -174,6 +174,17 @@ python -m gongwen style-learn 标准公文.docx -n 模板名
174
174
  python -m gongwen style-list # 列出已学习的模板
175
175
  ```
176
176
 
177
+ ### 🖥️ Windows 控制台编码(GBK 乱码排查)
178
+
179
+ 工具内部已统一按 UTF-8 输出(`gongwen/_bootstrap.py` 强制 stdout/stderr/stdin 重配置为 UTF-8,
180
+ 保证管道/Agent 调用无编码问题)。若在原生 `cmd`(默认 GBK 代码页 936)看到中文乱码,任选其一:
181
+
182
+ - **推荐**:改用 Windows Terminal / VS Code 终端(默认 UTF-8,无乱码)
183
+ - 在 cmd 中先执行 `chcp 65001` 切换 UTF-8 代码页,再运行命令
184
+ - 或设置环境变量 `PYTHONIOENCODING=utf-8`(与工具内部行为一致)
185
+
186
+ > 说明:GBK 乱码仅影响原生 cmd 的**交互显示**,不影响文件内容与 `--json` 的机器可解析性。
187
+
177
188
  ### 🔤 字体管理
178
189
 
179
190
  公文标准字体是 GB/T 9704 排版的关键。项目内置 3 个标准字体文件(`assets/fonts/`),支持自动安装:
@@ -366,7 +377,7 @@ DSH 采用 **Cordis 模块化微内核架构**:技能体系基于本地文件
366
377
  git clone https://github.com/linhut/gongwen-skill.git
367
378
  cd gongwen-skill
368
379
  pip install -r requirements.txt # 或 pip install gongwen-skill(已上 PyPI)
369
- python -m gongwen --version # 检验:gongwen-skill v2.9.0
380
+ python -m gongwen --version # 检验:gongwen-skill v2.10.0
370
381
  ```
371
382
 
372
383
  ### 方式一:作为 DSH Skill 注册(基于本地文件系统)
@@ -422,7 +433,7 @@ pnpm add -w gongwen-skill
422
433
  "dependencies": {
423
434
  "@deepseek-ai/dsh-base": "...",
424
435
  "@deepseek-ai/dsh-web-app": "...",
425
- "gongwen-skill": "^2.9.0"
436
+ "gongwen-skill": "^2.10.0"
426
437
  },
427
438
  "dsh": {
428
439
  "profile": {
@@ -611,7 +622,7 @@ pip install -r requirements.txt
611
622
  用户:帮我优化这份会议通知的第二章节措辞
612
623
 
613
624
  Agent:📋 合规自检报告
614
- Skill 版本: v2.9.0(版本自检已确认最新)
625
+ Skill 版本: v2.10.0(版本自检已确认最新)
615
626
  路径判定: B(内容优化)
616
627
  依据: 用户指定了已有文档,且要求"优化措辞"
617
628
  命令调用: 1. python -m gongwen optimize-content 会议通知.docx --changes changes.json --apply --paragraphs "5-8"
@@ -655,7 +666,7 @@ Skill 定位为**工具层**,默认不依赖 LLM(确定性工作全自包含
655
666
 
656
667
  **原理**:安全 DNS(DoH,DNS over HTTPS)通过加密 HTTP 查询 DNS,避免中间设备篡改解析结果,可拿到域名的真实 IP。本工具内置阿里(dns.alidns.com)、腾讯(doh.pub / 1.12.12.12)等国内公共 DoH 端点,多端点自动降级;可通过环境变量 GONGWEN_DOH 覆盖为自定义端点(如自建的 DoH 服务)。
657
668
 
658
- **自动兜底(v2.9.0)**:`font install` 下载字体、`check-update` 查 PyPI 时若常规请求失败(疑似 DNS 污染),自动用 DoH 真实 IP + TLS SNI 直连重试——TLS 证书仍按真实域名校验,安全不降级,用户零操作。
669
+ **自动兜底(v2.10.0)**:`font install` 下载字体、`check-update` 查 PyPI 时若常规请求失败(疑似 DNS 污染),自动用 DoH 真实 IP + TLS SNI 直连重试——TLS 证书仍按真实域名校验,安全不降级,用户零操作。
659
670
 
660
671
  **处置建议**(按推荐度):
661
672
  1. 若使用了代理工具(Clash/V2Ray 等)且系统解析命中 198.18.x Fake-IP,优先检查其 DNS 模式的 fake-ip-filter 是否漏掉 GitHub 域名(比改 hosts 更治本)
package/dsh/index.js CHANGED
@@ -1,4 +1,4 @@
1
- // 公文全流程处理工具 - DSH plugin bridge (gongwen-skill, v2.9.0+)
1
+ // 公文全流程处理工具 - DSH plugin bridge (gongwen-skill, v2.10.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.
@@ -107,6 +107,12 @@ def check_document(model: DocumentModel, rules: dict[str, Any]) -> list[CheckIss
107
107
  # 单元格边距)——此前 _common.yaml 的 table 配置块为死配置,无任何 CHK 规则
108
108
  issues.extend(_check_table_style(model, rule_id, severity, name, field_path,
109
109
  expected, message))
110
+ elif field_path.startswith("language."):
111
+ # 整改补强:CHK-L003(language.formal 语言规范)等语义类规则——
112
+ # 语言是否规范/得体/简明属人工或 LLM 审校范畴(optimize-content/review 覆盖),
113
+ # 规则引擎无法机械判定;保留规则定义(文档意图),静默跳过避免告警噪音
114
+ logger.debug("check_document: 语义类检查字段 '%s'(rule %s)由人工/LLM 审校覆盖,跳过",
115
+ field_path, rule_id)
110
116
  else:
111
117
  # P1-6 修复:删除 generic else 中的硬编码索引逻辑(model.paragraphs[0]/[1]
112
118
  # 不一定是标题/正文,检查结果会指向错误段落),未识别的 field 直接 skip + warning
@@ -7,7 +7,7 @@
7
7
  #
8
8
  # 公文全流程处理工具 - gongwen-skill Python package
9
9
 
10
- __version__ = "2.9.0"
10
+ __version__ = "2.10.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.
@@ -8,7 +8,7 @@
8
8
  # python -m gongwen entry point
9
9
  # Usage: python -m gongwen check input.docx -t notice
10
10
 
11
- from gongwen._legacy import main
11
+ from gongwen.cli.app import main
12
12
 
13
13
  if __name__ == "__main__":
14
14
  main()
@@ -10,52 +10,13 @@
10
10
  # 本文件为独立发行版的入口,任何人克隆仓库后即可运行,
11
11
  # 无需原桌面端项目、无需数据库、无需后端服务。
12
12
 
13
- from gongwen.cli.content_cmds import (
14
- cmd_optimize_content,
15
- )
16
- from gongwen.cli.misc_cmds import (
17
- cmd_rule_export,
18
- cmd_rule_list,
19
- cmd_rule_import,
20
- cmd_table_signs,
21
- cmd_audit,
22
- cmd_style_learn,
23
- cmd_style_list,
24
- cmd_review,
25
- )
26
- from gongwen.cli.review_cmds import (
27
- cmd_full_review,
28
- cmd_bold_first,
29
- cmd_fix_common,
30
- cmd_handoff,
31
- )
32
- from gongwen.cli.draft_cmds import (
33
- cmd_draft,
34
- )
35
- from gongwen.cli.update_cmds import (
36
- cmd_check_update,
37
- )
38
- from gongwen.cli.font_cmds import (
39
- cmd_font,
40
- )
41
- from gongwen.cli.doctor_cmds import (
42
- cmd_doctor,
43
- cmd_repair,
44
- )
45
- from gongwen.cli.wizard_cmds import (
46
- cmd_wizard,
47
- )
48
13
  from gongwen.cli.helpers import (
49
14
  detect_doc_type as _detect_doc_type,
50
15
  build_output_name as _build_output_name,
51
16
  parse_config_overrides as _parse_config_overrides,
52
17
  load_rules_with_overrides as _load_rules_with_overrides,
53
18
  )
54
- from engine.core.document.font_utils import (
55
- PAGE_NUMBER_FONT,
56
- PAGE_NUMBER_SIZE_PT,
57
- )
58
- __version__ = "2.9.0"
19
+ __version__ = "2.10.0"
59
20
  # 版本号应与 gongwen/__init__.py 保持一致,每次发版同步更新
60
21
  """
61
22
  中文公文全流程处理工具 —— 基于 GB/T 9704《党政机关公文格式》国家标准。
@@ -91,7 +52,6 @@ __version__ = "2.9.0"
91
52
  python -m gongwen footer in.docx --cc 各省民委 --printer 国家民委办公厅 --print-date 2026年7月23日
92
53
  python -m gongwen pagenum in.docx --alignment right
93
54
  """
94
- import argparse # noqa: E402
95
55
  import json # noqa: E402
96
56
  import logging # noqa: E402
97
57
  import sys # noqa: E402
@@ -173,6 +133,10 @@ def cmd_parse(args):
173
133
  text = json.dumps(data, ensure_ascii=False, indent=2)
174
134
  if args.output:
175
135
  Path(args.output).write_text(text, encoding="utf-8")
136
+ if getattr(args, "json", False):
137
+ # --json:强制完整 JSON 到 stdout(即使指定了 -o),Agent 可统一解析
138
+ print(text)
139
+ elif args.output:
176
140
  print(f"已解析: {args.output} ({len(model.paragraphs)} 段落, {len(model.tables)} 表格)")
177
141
  else:
178
142
  print(text)
@@ -265,6 +229,7 @@ def cmd_optimize(args):
265
229
  "fixed": 0, "cleaned": 0, "bolded": 0, "blank_lines": 0,
266
230
  "ai_declaration_removed": bool(getattr(args, "remove_ai_declaration", False)),
267
231
  "verified": None, "verify_issues": None,
232
+ "verify_executed": False, "verify_passed": False,
268
233
  "verify_p0": None, "verify_p1": None, "verify_p2": None,
269
234
  "verify_error": None,
270
235
  }
@@ -353,6 +318,7 @@ def cmd_optimize(args):
353
318
 
354
319
  # === 验证闭环(--verify,路径 A 单命令)===
355
320
  if getattr(args, "verify", False) and out.exists():
321
+ result["verify_executed"] = True
356
322
  try:
357
323
  v_model = parse_docx(str(out))
358
324
  v_issues = engine.check(v_model, doc_type)
@@ -360,6 +326,7 @@ def cmd_optimize(args):
360
326
  v_p1 = sum(1 for i in v_issues if i.severity == "P1")
361
327
  v_p2 = sum(1 for i in v_issues if i.severity == "P2")
362
328
  result["verified"] = True
329
+ result["verify_passed"] = (v_p0 == 0)
363
330
  result["verify_issues"] = len(v_issues)
364
331
  result["verify_p0"] = v_p0
365
332
  result["verify_p1"] = v_p1
@@ -846,10 +813,21 @@ def cmd_md2docx(args):
846
813
  except Exception as e:
847
814
  print(f" ⚠️ 页码注入失败({e}),跳过", file=sys.stderr)
848
815
 
849
- print(f"公文已生成: {out}")
850
- print(f" 类型: {doc_type}, 段落: {len(model.paragraphs)}, Markdown 转换: {changes} 处")
851
- if source_desc != "stdin" and args.input != "-":
852
- print(f" 来源: {source_desc}")
816
+ if getattr(args, "json", False):
817
+ # --json:结构化摘要(Agent 可直接解析)
818
+ print(json.dumps({
819
+ "command": "md2docx",
820
+ "output": str(out),
821
+ "doc_type": doc_type,
822
+ "paragraphs": len(model.paragraphs),
823
+ "markdown_changes": changes,
824
+ "source": source_desc if source_desc != "stdin" else "stdin",
825
+ }, ensure_ascii=False))
826
+ else:
827
+ print(f"公文已生成: {out}")
828
+ print(f" 类型: {doc_type}, 段落: {len(model.paragraphs)}, Markdown 转换: {changes} 处")
829
+ if source_desc != "stdin" and args.input != "-":
830
+ print(f" 来源: {source_desc}")
853
831
 
854
832
 
855
833
  def cmd_header(args):
@@ -951,350 +929,10 @@ _STYLE_DEVIATION_HINTS = {
951
929
  FONTS_DOWNLOAD_BASE = "https://raw.githubusercontent.com/linhut/document-ai-assistant/master/TTF"
952
930
 
953
931
 
954
- # ---------------------------------------------------------------------------
955
- # 参数解析
956
- # ---------------------------------------------------------------------------
957
-
958
- # O8:命令分组(--help 按场景展示;分组覆盖全部子命令,未列出的自动归入「其他」)
959
- COMMAND_GROUPS = [
960
- ("🏗️ 生成", ["list-types", "template", "generate", "md2docx", "draft", "style-learn", "style-list"]),
961
- ("🔧 格式", ["parse", "check", "optimize", "fix-common", "bold-first"]),
962
- ("✍️ 内容", ["optimize-content", "review", "handoff", "wizard"]),
963
- ("🔎 审校", ["full-review", "audit"]),
964
- ("🎨 版式", ["header", "footer", "pagenum", "font", "table-signs"]),
965
- ("⚙️ 运维", ["rule-export", "rule-list", "rule-import", "check-update", "doctor", "repair"]),
966
- ]
967
-
968
-
969
- class _GroupedHelpFormatter(argparse.RawDescriptionHelpFormatter):
970
- """O8:--help 按 COMMAND_GROUPS 分组展示子命令(保留 usage/options/epilog 原样)。"""
971
-
972
- def _format_action(self, action):
973
- if not isinstance(action, argparse._SubParsersAction):
974
- return super()._format_action(action)
975
- # 子命令分组渲染:_choices_actions 保存 dest(命令名)+ help(add_parser 的 help)
976
- pseudo = {ca.dest: ca for ca in getattr(action, "_choices_actions", [])}
977
- names = list(pseudo.keys())
978
- if not names:
979
- return super()._format_action(action)
980
- col = max((len(n) for n in names), default=10) + 2
981
- lines = []
982
- grouped = set()
983
- for group_title, cmds in COMMAND_GROUPS:
984
- valid = [c for c in cmds if c in pseudo]
985
- if not valid:
986
- continue
987
- grouped.update(valid)
988
- lines.append(f"{group_title}:")
989
- for n in valid:
990
- h = pseudo[n].help or ""
991
- lines.append(f" {n:<{col}}{h}")
992
- others = [n for n in names if n not in grouped]
993
- if others:
994
- lines.append("📌 其他:")
995
- for n in others:
996
- h = pseudo[n].help or ""
997
- lines.append(f" {n:<{col}}{h}")
998
- return "\n".join(lines)
999
-
1000
-
1001
932
  def main():
1002
- parser = argparse.ArgumentParser(
1003
- prog="gongwen",
1004
- description="公文全流程处理工具(GB/T 9704)—— 格式检查/内容优化/模板生成/版式注入 (c) 2026 Jose AI https://www.linhut.cn",
1005
- formatter_class=_GroupedHelpFormatter,
1006
- epilog=__doc__,
1007
- )
1008
- parser.add_argument("--version", action="version", version=f"公文全流程处理工具 v{__version__}",
1009
- help="显示版本号并退出")
1010
- sub = parser.add_subparsers(dest="command", help="子命令")
1011
-
1012
- p = sub.add_parser("list-types", help="列出支持的公文类型")
1013
- p.add_argument("--json", action="store_true", help="JSON 输出")
1014
- p.set_defaults(func=cmd_list_types)
1015
-
1016
- p = sub.add_parser("template", help="生成标准公文模板")
1017
- p.add_argument("type", help="公文类型(见 list-types)")
1018
- p.add_argument("-o", "--output", help="输出文件路径")
1019
- p.add_argument("--config-overrides", default="",
1020
- help="规则覆盖 JSON(DSH 插件注入,如 '{\"body\":{\"line_spacing\":\"28pt\"}}')")
1021
- p.set_defaults(func=cmd_template)
1022
-
1023
- p = sub.add_parser("parse", help="解析文档为结构化 JSON")
1024
- p.add_argument("input", help="输入 .docx 路径")
1025
- p.add_argument("-o", "--output", help="输出 JSON 路径(缺省打印到 stdout)")
1026
- p.set_defaults(func=cmd_parse)
1027
-
1028
- p = sub.add_parser("check", help="检查文档格式(只读)")
1029
- p.add_argument("input", help="输入 .docx 路径")
1030
- p.add_argument("-t", "--doc-type", default="notice", help="公文类型(默认 notice)")
1031
- p.add_argument("-s", "--severity", choices=["P0", "P1", "P2"], help="仅显示指定级别")
1032
- p.add_argument("--json", action="store_true", help="JSON 输出")
1033
- p.add_argument("--config-overrides", default="",
1034
- help="规则覆盖 JSON(DSH 插件注入)")
1035
- p.set_defaults(func=cmd_check)
1036
-
1037
- p = sub.add_parser("optimize", help="检查 + 修复 + 生成(预览模式默认,--apply 执行)")
1038
- p.add_argument("input", help="输入 .docx 路径")
1039
- p.add_argument("-o", "--output", help="输出 .docx 路径")
1040
- p.add_argument("-t", "--doc-type", default="", help="公文类型(默认自动检测,可指定)")
1041
- p.add_argument("--selected-rules", help="仅应用指定修复规则 ID,逗号分隔")
1042
- p.add_argument("--layout", help="版式注入 JSON 配置(含 header/footer/page_number)")
1043
- p.add_argument("--apply", action="store_true", help="确认执行修复(默认预览)")
1044
- p.add_argument("--verify", action="store_true", help="执行后自动 check 输出文件(存在 P0 时退出码非 0,单命令闭环)")
1045
- p.add_argument("--json", action="store_true", help="JSON 结构化输出(Agent 可机器解析)")
1046
- p.add_argument("--remove-ai-declaration", action="store_true",
1047
- help="P1: 生成文档不追加 AI 声明段(默认追加)")
1048
- p.add_argument("--config-overrides", default="",
1049
- help="规则覆盖 JSON(DSH 插件注入)")
1050
- p.set_defaults(func=cmd_optimize)
1051
-
1052
- p = sub.add_parser("generate", help="从 DocumentModel JSON 生成 .docx")
1053
- p.add_argument("input", help="输入 model.json 路径")
1054
- p.add_argument("-o", "--output", help="输出 .docx 路径")
1055
- p.set_defaults(func=cmd_generate)
1056
-
1057
- p = sub.add_parser("header", help="注入版头:发文机关标志 + 发文字号 + 签发人 + 红色反线")
1058
- p.add_argument("input", help="输入 .docx 路径")
1059
- p.add_argument("-o", "--output", help="输出 .docx 路径(默认原地修改)")
1060
- p.add_argument("--org-name", required=True, help="发文机关标志(红色大字,必填)")
1061
- p.add_argument("--doc-number", default="", help="发文字号,如 XX〔2026〕1号")
1062
- p.add_argument("--signer", default="", help="签发人姓名(上行文)")
1063
- p.set_defaults(func=cmd_header)
1064
-
1065
- p = sub.add_parser("footer", help="注入版记:抄送 + 印发机关 + 印发日期 + 分隔线")
1066
- p.add_argument("input", help="输入 .docx 路径")
1067
- p.add_argument("-o", "--output", help="输出 .docx 路径(默认原地修改)")
1068
- p.add_argument("--cc", default="", help="抄送机关")
1069
- p.add_argument("--printer", default="", help="印发机关")
1070
- p.add_argument("--print-date", default="", help="印发日期")
1071
- p.set_defaults(func=cmd_footer)
1072
-
1073
- p = sub.add_parser("pagenum", help="注入页码:Word PAGE 域动态页码")
1074
- p.add_argument("input", help="输入 .docx 路径")
1075
- p.add_argument("-o", "--output", help="输出 .docx 路径(默认原地修改)")
1076
- p.add_argument("--font", default=PAGE_NUMBER_FONT, help="页码字体(默认 宋体)")
1077
- p.add_argument("--size", type=int, default=PAGE_NUMBER_SIZE_PT, help="页码字号(默认 14)")
1078
- p.add_argument("--alignment", default="right",
1079
- choices=["center", "left", "right"],
1080
- help="对齐(默认 right 单右双左奇偶排版,适配双面打印;center 居中;left 左对齐)")
1081
- p.add_argument("--format", default="- {PAGE} -",
1082
- help="页码格式,可用 {PAGE} / {NUMPAGES}(默认 '- {PAGE} -')")
1083
- p.set_defaults(func=cmd_pagenum)
1084
-
1085
- p = sub.add_parser("md2docx", help="将 Markdown 文本转为格式化的公文 .docx")
1086
- p.add_argument("input", help="输入 .md 路径,或 '-' 从标准输入读取(支持管道)")
1087
- p.add_argument("-o", "--output", help="输出 .docx 路径(默认 output.docx)")
1088
- p.add_argument("-t", "--doc-type", default=None, help="公文类型(默认 notice,可被 Front Matter 覆盖)")
1089
- p.add_argument("--recipients", nargs="*", help="主送机关(逗号分隔)")
1090
- p.add_argument("--signer", default="", help="落款单位")
1091
- p.add_argument("--date", default="", help="成文日期")
1092
- p.add_argument("--attachments", nargs="*", help="附件列表")
1093
- p.add_argument("--no-ai-declaration", action="store_true",
1094
- help="P1: 生成文档不追加 AI 声明段(默认追加)")
1095
- p.add_argument("--config-overrides", default="",
1096
- help="规则覆盖 JSON(DSH 插件注入)")
1097
- p.set_defaults(func=cmd_md2docx)
1098
-
1099
- # ---- draft:路径 C 一站式生成(Markdown → 国标成品 + 验证)----
1100
- p = sub.add_parser("draft", help="Markdown 草稿 → 国标成品 + 验证(路径 C 四步合一)")
1101
- p.add_argument("input", help="输入 .md 路径,或 '-' 从标准输入读取(支持管道)")
1102
- p.add_argument("-o", "--output", help="输出 .docx 路径(默认 修订版+{类型}-成品+{日期}+v1.docx)")
1103
- p.add_argument("-t", "--doc-type", default="", help="公文类型(默认自动检测/Front Matter,可指定)")
1104
- p.add_argument("--recipients", nargs="*", help="主送机关(逗号分隔)")
1105
- p.add_argument("--signer", default="", help="落款单位")
1106
- p.add_argument("--date", default="", help="成文日期")
1107
- p.add_argument("--attachments", nargs="*", help="附件列表")
1108
- p.add_argument("--no-ai-declaration", action="store_true",
1109
- help="生成的文档不追加 AI 声明段(默认追加)")
1110
- p.add_argument("--verify", dest="verify", action="store_true", default=True,
1111
- help="生成后自动 check 验证(默认开启;P0 存在时退出码非 0)")
1112
- p.add_argument("--no-verify", dest="verify", action="store_false",
1113
- help="跳过生成后验证")
1114
- p.add_argument("--json", action="store_true", help="JSON 结构化输出(Agent 可机器解析)")
1115
- p.add_argument("--config-overrides", default="",
1116
- help="DSH 配置覆盖 JSON 字符串(页边距/行距/字体)")
1117
- p.set_defaults(func=cmd_draft)
1118
-
1119
- p = sub.add_parser("optimize-content", help="内容优化差异对比:原文灰色+删除线,修改后红色高亮,每段附修改说明与依据")
1120
- p.add_argument("input", help="输入 .docx 路径")
1121
- p.add_argument("-o", "--output", help="输出 .docx 路径(默认按规范自动命名:{原文档名}+{内容风格}+{日期}+v1.docx)")
1122
- p.add_argument("--changes", required=False, default="",
1123
- help="变更 JSON 文件路径(含 paragraph_index/original_text/optimized_text/reason/reference);"
1124
- "不提供且加 --auto-generate 时基于内置规则自动生成")
1125
- p.add_argument("--optimize-format", action="store_true", help="同时优化格式(默认仅做差异标注,不改格式)")
1126
- p.add_argument("--apply", action="store_true", help="确认生成差异对比文档(默认预览)")
1127
- p.add_argument("--disclaimer", default=None, help="文档末尾 AI 声明文字(默认:内容由GongWen-skill-AI生成,仅供参考)")
1128
- p.add_argument("--force", action="store_true", help="强制替换:文本匹配失败时直接替换段落全部内容(可能丢失加粗等格式)")
1129
- p.add_argument("--paragraphs", type=str, default=None, help='只处理指定段落范围,如 "11-15" 或 "5,7,9"')
1130
- p.add_argument("--comment-mode", action="store_true",
1131
- help="批注模式:将优化建议以 Word 原生批注写入(可审阅→接受/拒绝),而非行内标记")
1132
- p.add_argument("--tracked-change", action="store_true",
1133
- help="修订追踪模式:将修改以 Word 原生修订标记(ins/del)写入,可在审阅面板逐条接受/拒绝")
1134
- p.add_argument("--preset", default="", choices=["quick", "full", "review"],
1135
- help="O6:预设组合——quick 精简快速(3角色+跳过风格增强)/ full 完整默认(6角色+风格增强+事实核验)/ review 完整审稿(6角色+已确认实体批注);显式参数优先于预设")
1136
- p.add_argument("--mode", default="tracked", choices=["inline", "tracked"],
1137
- help="输出模式:tracked 修订+批注(默认,Word 审阅面板逐条接受/拒绝,修改说明写入批注);inline 行内标记(显式降级选择)")
1138
- p.add_argument("--reviewers", type=int, default=argparse.SUPPRESS, choices=[3, 5, 6],
1139
- help="审稿角色数:6 完整版(默认,含事实核验员)/ 5 完整版(历史兼容,同6)/ 3 精简版,意见作为独立批注按审阅者写入")
1140
- p.add_argument("--background", nargs="*", default=None,
1141
- help="背景资料路径(事实核验用,支持多个):.docx / .pdf / .md / .txt / URL,与 --mode tracked 配合对存疑人事信息生成批注提醒")
1142
- p.add_argument("--perspective", default="",
1143
- help="P2 修复:优化视角/风格方向(路径B第0步确认,影响审稿方向和修改说明标注),如 '务实客观,数据驱动,避免主观评价和万能结论'")
1144
- p.add_argument("--show-confirmed", action="store_true",
1145
- help="P7 修复:对已确认实体也生成「✅已确认」批注(默认不生成,避免噪音)")
1146
- p.add_argument("-t", "--doc-type", default="", help="改进 A:显式指定公文类型(默认自动检测)")
1147
- p.add_argument("--show-rules", action="store_true",
1148
- help="改进 A:输出当前文档类型的内容层规则摘要(structure/focus_checks)")
1149
- p.add_argument("--auto-generate", action="store_true",
1150
- help="改进 E:无 --changes 时基于内置规则+风格提示词自动生成优化建议(需配置 GONGWEN_LLM_API)")
1151
- p.add_argument("--output-tasks", default="",
1152
- help="V1:将待 Agent 处理的任务(事实核验实体/风格增强请求)输出到 JSON 文件,同时生成基础版文档")
1153
- p.add_argument("--input-tasks", default="",
1154
- help="V1:读入 Agent 回填的 tasks_result.json,将事实核验修正/风格建议合并到 changes 后执行")
1155
- p.add_argument("--style", default="",
1156
- help="V3:语言风格。不指定则自动推断(changes.style → doc_type 映射 → 默认庄重严谨)")
1157
- p.add_argument("--no-style-enhance", action="store_true",
1158
- help="V3:禁用风格增强步骤(默认启用)")
1159
- p.add_argument("--quiet", action="store_true", help="安静模式:仅输出最终结果,不显示分步进度")
1160
- p.add_argument("--precheck", action="store_true",
1161
- help="O7:预检模式——逐段比对 changes.json 与原文,输出不匹配清单和相似度诊断(不生成文档)")
1162
- p.add_argument("--json", action="store_true",
1163
- help="JSON 输出(与 --precheck 配合输出结构化预检结果)")
1164
- # P1-8 修复:移除从未使用的 --verbose 参数(cmd 函数内无任何引用)
1165
- p.set_defaults(func=cmd_optimize_content)
1166
-
1167
- p = sub.add_parser("bold-first", help="正文段落首句加粗(符合公文规范:点题第一句话默认加粗)")
1168
- p.add_argument("input", help="输入 .docx 路径")
1169
- p.add_argument("-o", "--output", help="输出 .docx 路径(默认输入_加粗首句.docx)")
1170
- p.set_defaults(func=cmd_bold_first)
1171
-
1172
- p = sub.add_parser("fix-common", help="一键修复常见格式问题(路径D):段落类型修正/编号拆分/首句加粗/加粗范围修复,不含AI声明段")
1173
- p.add_argument("--json", action="store_true", help="JSON 结构化输出(Agent 可机器解析)")
1174
- p.add_argument("input", help="输入 .docx 路径")
1175
- p.add_argument("-o", "--output", help="输出 .docx 路径(默认输入_fix-common.docx)")
1176
- p.set_defaults(func=cmd_fix_common)
1177
-
1178
- p = sub.add_parser("handoff", help="查看/写入会话交接文档(跨会话上下文传递,长任务收尾必写)")
1179
- p.add_argument("--list", action="store_true", help="列出所有交接文档摘要")
1180
- p.add_argument("--latest", action="store_true", help="读取最新交接文档(JSON,加 --summary 输出 Markdown 摘要)")
1181
- p.add_argument("--summary", action="store_true", help="以 Markdown 摘要输出(配合 --latest)")
1182
- p.add_argument("--write", metavar="JSON_PATH", help="从 JSON 文件写入交接文档(P2-27)")
1183
- p.set_defaults(func=cmd_handoff)
1184
-
1185
- p = sub.add_parser("rule-export", help="导出合并后的规则为 YAML")
1186
- p.add_argument("type", help="公文类型")
1187
- p.add_argument("-o", "--output", help="输出 YAML 路径")
1188
- p.set_defaults(func=cmd_rule_export)
1189
-
1190
- p = sub.add_parser("rule-list", help="列出三层规则")
1191
- p.add_argument("--source", default="all", choices=["all", "official", "custom", "user"])
1192
- p.add_argument("--json", action="store_true", help="JSON 输出")
1193
- p.set_defaults(func=cmd_rule_list)
1194
-
1195
- p = sub.add_parser("rule-import", help="导入/保存自定义规则 YAML")
1196
- p.add_argument("key", help="规则标识符(仅字母数字下划线连字符)")
1197
- p.add_argument("-f", "--file", help="YAML 文件路径")
1198
- p.add_argument("--text", help="YAML 文本内容(内联)")
1199
- p.add_argument("--source", default="user", choices=["user", "custom"], help="保存层级")
1200
- p.set_defaults(func=cmd_rule_import)
1201
-
1202
- # ---- 桌签批量生成 ----
1203
- p = sub.add_parser("table-signs", help="从名单批量生成双面桌签(A4纵向,华文新魏,字号按名长动态调整),每人一份或合并多页")
1204
- p.add_argument("input", help="名单文件路径(每行一人,支持逗号/空格/顿号分隔),或 '-' 从标准输入读取")
1205
- p.add_argument("-o", "--output", help="输出目录(默认 ./桌签/;每人单独文件)或输出 .docx 路径(配合 --combined)")
1206
- p.add_argument("--combined", action="store_true", help="合并为一个多页文档(默认每人一个独立文件)")
1207
- p.add_argument('--prefix', default='桌签', help='独立文件时文件名前缀(默认"桌签")')
1208
- # P0-7 修复:--template 改非必填——方案六已内置默认模板(engine/templates/table_sign.dotx),
1209
- # 未传时走默认模板,实现零配置;传了则覆盖
1210
- p.add_argument("--template",
1211
- help="座签模板 .dotx 路径(默认使用内置模板;如 F:/模板/座签模板.dotx)")
1212
- # 方案五(P2-3):占位符参数化
1213
- p.add_argument("--placeholder", default="Jose AI",
1214
- help="座签模板中占位文本(默认 Jose AI)")
1215
- p.set_defaults(func=cmd_table_signs)
1216
-
1217
- # ---- 完整审校(路径A + 路径B + 批注) ----
1218
- p = sub.add_parser("full-review", help="完整审校:格式修复(路径A)→ 内容优化(路径B)→ 批注输出,一条命令完成")
1219
- p.add_argument("input", help="输入 .docx 路径")
1220
- p.add_argument("-o", "--output", help="输出 .docx 路径")
1221
- p.add_argument("-t", "--doc-type", default="", help="公文类型(默认自动检测)")
1222
- p.add_argument("--changes", default="", help="变更 JSON 文件路径(路径B优化建议,可省略则仅格式修复+批注空)")
1223
- p.add_argument("--json", action="store_true", help="JSON 结构化输出(Agent 可机器解析)")
1224
- p.set_defaults(func=cmd_full_review)
1225
-
1226
- # ---- 样式学习(上传标准文档 → 自定义命名模板) ----
1227
- p = sub.add_parser("style-learn", help="从标准 .docx 文档学习排版样式(含字间距等细微属性),生成自定义命名模板并注册")
1228
- p.add_argument("input", help="输入标准 .docx 文档路径(如单位定稿红头公文)")
1229
- p.add_argument("-n", "--name", default="", help="模板名(默认 自定义_{文档名}),注册后可用 optimize -t {模板名}")
1230
- p.set_defaults(func=cmd_style_learn)
1231
-
1232
- p = sub.add_parser("style-list", help="列出所有通过 style-learn 学习的自定义样式模板")
1233
- p.set_defaults(func=cmd_style_list)
1234
-
1235
- # ---- 版本自检(PyPI pip 包权威判定,GitHub 备用,GitCode/AtomGit 作国内镜像) ----
1236
- p = sub.add_parser("check-update", help="版本自检:PyPI(pip 包权威)判定更新,GitHub 作备用渠道(GitCode/AtomGit 作国内镜像提示)")
1237
- p.add_argument("--json", action="store_true",
1238
- help="输出 JSON 格式结果(便于 Agent 解析)")
1239
- p.set_defaults(func=cmd_check_update)
1240
-
1241
- # ---- 文档审计 ----
1242
- p = sub.add_parser("audit", help="审计文档处理链:检查删除线、加粗、AI声明等合规性问题")
1243
- p.add_argument("input", help="输入 .docx 路径")
1244
- p.set_defaults(func=cmd_audit)
1245
-
1246
- # ---- 审稿流转单生成 ----
1247
- p = sub.add_parser("review", help="生成公文审稿流转单(五角色/三角色审核模板)")
1248
- p.add_argument("doc_type", help="公文类型,如 通知/请示/报告/函")
1249
- p.add_argument("-o", "--output", help="输出 .docx 路径(默认 审稿流转单-{type}.docx)")
1250
- p.add_argument("--scheme", default="full", choices=["full", "compact"],
1251
- help="审稿方案:full=完整五角色(默认), compact=精简三角色")
1252
- p.add_argument("--title", default="", help="待审文稿标题")
1253
- p.set_defaults(func=cmd_review)
1254
-
1255
- # ---- 向导式交互 ----
1256
- p = sub.add_parser("wizard", help="向导式交互:A/B/C/D/E 路径引导 + 一键执行(--answers 非交互 / --dry-run 只打印)")
1257
- p.add_argument("--answers", default="",
1258
- help="答案 JSON 文件路径(Agent 非交互模式):"
1259
- '{"path":"A","input":"a.docx","apply":true}')
1260
- p.add_argument("--dry-run", action="store_true", help="只打印将执行的命令,不真正执行")
1261
- p.set_defaults(func=cmd_wizard)
1262
-
1263
- # ---- 字体管理 ----
1264
- p = sub.add_parser("font", help="公文标准字体管理:安装/检查/列出内置字体")
1265
- p.add_argument("action", nargs="?", default="list",
1266
- choices=["list", "check", "install"],
1267
- help="list=列出字体清单, check=检查安装状态, install=安装字体")
1268
- p.set_defaults(func=cmd_font)
1269
-
1270
- # ---- 自我诊断与修复 ----
1271
- p = sub.add_parser("doctor", help="全面诊断:检查 Python 版本/依赖/版本一致性/字体/DSH 文件/代码风格/网络 DNS 等")
1272
- p.add_argument("--json", action="store_true",
1273
- help="输出 JSON 格式结果(便于 Agent 解析)")
1274
- p.add_argument("--offline", action="store_true",
1275
- help="跳过网络/DNS 诊断(离线模式)")
1276
- p.set_defaults(func=cmd_doctor)
1277
-
1278
- p = sub.add_parser("repair", help="修复常见问题:安装缺失依赖/字体/同步 SKILL.md 副本")
1279
- p.set_defaults(func=cmd_repair)
1280
-
1281
- args = parser.parse_args()
1282
-
1283
- if not args.command:
1284
- parser.print_help()
1285
- sys.exit(1)
1286
-
1287
- try:
1288
- # P3-22 修复:捕获子命令返回值(如 check-update 的退出码),非零则用于进程退出码
1289
- ret = args.func(args)
1290
- if isinstance(ret, int) and ret != 0:
1291
- sys.exit(ret)
1292
- except FileNotFoundError as e:
1293
- print(f"错误:文件不存在 - {e}", file=sys.stderr)
1294
- sys.exit(1)
1295
- except Exception as e:
1296
- print(f"错误:{e}", file=sys.stderr)
1297
- sys.exit(1)
933
+ """CLI 主入口(已迁移至 gongwen.cli.app.main,保留兼容转发)。"""
934
+ from gongwen.cli.app import main as _main
935
+ return _main()
1298
936
 
1299
937
 
1300
938
  if __name__ == "__main__":
@@ -6,7 +6,7 @@
6
6
  # Licensed under the MIT License. See the LICENSE file for details.
7
7
  #
8
8
  # CLI module - lazy import to avoid circular dependency
9
- # main() is available via: from gongwen._legacy import main
9
+ # main() 入口已迁移至 gongwen.cli.app(整改 C);_legacy.py 保留兼容转发
10
10
 
11
11
  # helpers 模块可直接导入
12
12
  from gongwen.cli import helpers # noqa: F401
@@ -0,0 +1,406 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ #
4
+ # (c) 2026 Jose AI (https://www.linhut.cn)
5
+ # https://github.com/linhut/gongwen-skill
6
+ # Licensed under the MIT License. See the LICENSE file for details.
7
+ #
8
+ # CLI 主入口与命令注册(整改 C:从 gongwen/_legacy.py 迁出)。
9
+ # 命令实现在 gongwen/cli/ 各模块与 gongwen/_legacy.py(兼容层)。
10
+ #
11
+ import argparse
12
+ import sys
13
+
14
+ from gongwen._legacy import (
15
+ __doc__ as _LEGACY_DOC,
16
+ __version__,
17
+ cmd_list_types,
18
+ cmd_template,
19
+ cmd_parse,
20
+ cmd_check,
21
+ cmd_optimize,
22
+ cmd_generate,
23
+ cmd_md2docx,
24
+ cmd_header,
25
+ cmd_footer,
26
+ cmd_pagenum,
27
+ )
28
+ from gongwen.cli.content_cmds import cmd_optimize_content
29
+ from gongwen.cli.misc_cmds import (
30
+ cmd_rule_export,
31
+ cmd_rule_list,
32
+ cmd_rule_import,
33
+ cmd_table_signs,
34
+ cmd_audit,
35
+ cmd_style_learn,
36
+ cmd_style_list,
37
+ cmd_review,
38
+ )
39
+ from gongwen.cli.review_cmds import (
40
+ cmd_full_review,
41
+ cmd_bold_first,
42
+ cmd_fix_common,
43
+ cmd_handoff,
44
+ )
45
+ from gongwen.cli.draft_cmds import cmd_draft
46
+ from gongwen.cli.update_cmds import cmd_check_update
47
+ from gongwen.cli.font_cmds import cmd_font
48
+ from gongwen.cli.doctor_cmds import cmd_doctor, cmd_repair
49
+ from gongwen.cli.wizard_cmds import cmd_wizard
50
+ from engine.core.document.font_utils import (
51
+ PAGE_NUMBER_FONT,
52
+ PAGE_NUMBER_SIZE_PT,
53
+ )
54
+
55
+ # ---------------------------------------------------------------------------
56
+ # 参数解析
57
+ # ---------------------------------------------------------------------------
58
+
59
+ # O8:命令分组(--help 按场景展示;分组覆盖全部子命令,未列出的自动归入「其他」)
60
+ COMMAND_GROUPS = [
61
+ ("🏗️ 生成", ["list-types", "template", "generate", "md2docx", "draft", "style-learn", "style-list"]),
62
+ ("🔧 格式", ["parse", "check", "optimize", "fix-common", "bold-first"]),
63
+ ("✍️ 内容", ["optimize-content", "review", "handoff", "wizard"]),
64
+ ("🔎 审校", ["full-review", "audit"]),
65
+ ("🎨 版式", ["header", "footer", "pagenum", "font", "table-signs"]),
66
+ ("⚙️ 运维", ["rule-export", "rule-list", "rule-import", "check-update", "doctor", "repair"]),
67
+ ]
68
+
69
+
70
+ class _GroupedHelpFormatter(argparse.RawDescriptionHelpFormatter):
71
+ """O8:--help 按 COMMAND_GROUPS 分组展示子命令(保留 usage/options/epilog 原样)。"""
72
+
73
+ def _format_action(self, action):
74
+ if not isinstance(action, argparse._SubParsersAction):
75
+ return super()._format_action(action)
76
+ # 子命令分组渲染:_choices_actions 保存 dest(命令名)+ help(add_parser 的 help)
77
+ pseudo = {ca.dest: ca for ca in getattr(action, "_choices_actions", [])}
78
+ names = list(pseudo.keys())
79
+ if not names:
80
+ return super()._format_action(action)
81
+ col = max((len(n) for n in names), default=10) + 2
82
+ lines = []
83
+ grouped = set()
84
+ for group_title, cmds in COMMAND_GROUPS:
85
+ valid = [c for c in cmds if c in pseudo]
86
+ if not valid:
87
+ continue
88
+ grouped.update(valid)
89
+ lines.append(f"{group_title}:")
90
+ for n in valid:
91
+ h = pseudo[n].help or ""
92
+ lines.append(f" {n:<{col}}{h}")
93
+ others = [n for n in names if n not in grouped]
94
+ if others:
95
+ lines.append("📌 其他:")
96
+ for n in others:
97
+ h = pseudo[n].help or ""
98
+ lines.append(f" {n:<{col}}{h}")
99
+ return "\n".join(lines)
100
+
101
+
102
+ def main():
103
+ parser = argparse.ArgumentParser(
104
+ prog="gongwen",
105
+ description="公文全流程处理工具(GB/T 9704)—— 格式检查/内容优化/模板生成/版式注入 (c) 2026 Jose AI https://www.linhut.cn",
106
+ formatter_class=_GroupedHelpFormatter,
107
+ epilog=_LEGACY_DOC,
108
+ )
109
+ parser.add_argument("--version", action="version", version=f"公文全流程处理工具 v{__version__}",
110
+ help="显示版本号并退出")
111
+ sub = parser.add_subparsers(dest="command", help="子命令")
112
+
113
+ p = sub.add_parser("list-types", help="列出支持的公文类型")
114
+ p.add_argument("--json", action="store_true", help="JSON 输出")
115
+ p.set_defaults(func=cmd_list_types)
116
+
117
+ p = sub.add_parser("template", help="生成标准公文模板")
118
+ p.add_argument("type", help="公文类型(见 list-types)")
119
+ p.add_argument("-o", "--output", help="输出文件路径")
120
+ p.add_argument("--config-overrides", default="",
121
+ help="规则覆盖 JSON(DSH 插件注入,如 '{\"body\":{\"line_spacing\":\"28pt\"}}')")
122
+ p.set_defaults(func=cmd_template)
123
+
124
+ p = sub.add_parser("parse", help="解析文档为结构化 JSON")
125
+ p.add_argument("input", help="输入 .docx 路径")
126
+ p.add_argument("-o", "--output", help="输出 JSON 路径(缺省打印到 stdout)")
127
+ p.add_argument("--json", action="store_true", help="强制 JSON 输出到 stdout(与 -o 同时使用时也打印)")
128
+ p.set_defaults(func=cmd_parse)
129
+
130
+ p = sub.add_parser("check", help="检查文档格式(只读)")
131
+ p.add_argument("input", help="输入 .docx 路径")
132
+ p.add_argument("-t", "--doc-type", default="notice", help="公文类型(默认 notice)")
133
+ p.add_argument("-s", "--severity", choices=["P0", "P1", "P2"], help="仅显示指定级别")
134
+ p.add_argument("--json", action="store_true", help="JSON 输出")
135
+ p.add_argument("--config-overrides", default="",
136
+ help="规则覆盖 JSON(DSH 插件注入)")
137
+ p.set_defaults(func=cmd_check)
138
+
139
+ p = sub.add_parser("optimize", help="检查 + 修复 + 生成(预览模式默认,--apply 执行)")
140
+ p.add_argument("input", help="输入 .docx 路径")
141
+ p.add_argument("-o", "--output", help="输出 .docx 路径")
142
+ p.add_argument("-t", "--doc-type", default="", help="公文类型(默认自动检测,可指定)")
143
+ p.add_argument("--selected-rules", help="仅应用指定修复规则 ID,逗号分隔")
144
+ p.add_argument("--layout", help="版式注入 JSON 配置(含 header/footer/page_number)")
145
+ p.add_argument("--apply", action="store_true", help="确认执行修复(默认预览)")
146
+ p.add_argument("--verify", action="store_true", help="执行后自动 check 输出文件(存在 P0 时退出码非 0,单命令闭环)")
147
+ p.add_argument("--json", action="store_true", help="JSON 结构化输出(Agent 可机器解析)")
148
+ p.add_argument("--remove-ai-declaration", action="store_true",
149
+ help="P1: 生成文档不追加 AI 声明段(默认追加)")
150
+ p.add_argument("--config-overrides", default="",
151
+ help="规则覆盖 JSON(DSH 插件注入)")
152
+ p.set_defaults(func=cmd_optimize)
153
+
154
+ p = sub.add_parser("generate", help="从 DocumentModel JSON 生成 .docx")
155
+ p.add_argument("input", help="输入 model.json 路径")
156
+ p.add_argument("-o", "--output", help="输出 .docx 路径")
157
+ p.set_defaults(func=cmd_generate)
158
+
159
+ p = sub.add_parser("header", help="注入版头:发文机关标志 + 发文字号 + 签发人 + 红色反线")
160
+ p.add_argument("input", help="输入 .docx 路径")
161
+ p.add_argument("-o", "--output", help="输出 .docx 路径(默认原地修改)")
162
+ p.add_argument("--org-name", required=True, help="发文机关标志(红色大字,必填)")
163
+ p.add_argument("--doc-number", default="", help="发文字号,如 XX〔2026〕1号")
164
+ p.add_argument("--signer", default="", help="签发人姓名(上行文)")
165
+ p.set_defaults(func=cmd_header)
166
+
167
+ p = sub.add_parser("footer", help="注入版记:抄送 + 印发机关 + 印发日期 + 分隔线")
168
+ p.add_argument("input", help="输入 .docx 路径")
169
+ p.add_argument("-o", "--output", help="输出 .docx 路径(默认原地修改)")
170
+ p.add_argument("--cc", default="", help="抄送机关")
171
+ p.add_argument("--printer", default="", help="印发机关")
172
+ p.add_argument("--print-date", default="", help="印发日期")
173
+ p.set_defaults(func=cmd_footer)
174
+
175
+ p = sub.add_parser("pagenum", help="注入页码:Word PAGE 域动态页码")
176
+ p.add_argument("input", help="输入 .docx 路径")
177
+ p.add_argument("-o", "--output", help="输出 .docx 路径(默认原地修改)")
178
+ p.add_argument("--font", default=PAGE_NUMBER_FONT, help="页码字体(默认 宋体)")
179
+ p.add_argument("--size", type=int, default=PAGE_NUMBER_SIZE_PT, help="页码字号(默认 14)")
180
+ p.add_argument("--alignment", default="right",
181
+ choices=["center", "left", "right"],
182
+ help="对齐(默认 right 单右双左奇偶排版,适配双面打印;center 居中;left 左对齐)")
183
+ p.add_argument("--format", default="- {PAGE} -",
184
+ help="页码格式,可用 {PAGE} / {NUMPAGES}(默认 '- {PAGE} -')")
185
+ p.set_defaults(func=cmd_pagenum)
186
+
187
+ p = sub.add_parser("md2docx", help="将 Markdown 文本转为格式化的公文 .docx")
188
+ p.add_argument("input", help="输入 .md 路径,或 '-' 从标准输入读取(支持管道)")
189
+ p.add_argument("-o", "--output", help="输出 .docx 路径(默认 output.docx)")
190
+ p.add_argument("-t", "--doc-type", default=None, help="公文类型(默认 notice,可被 Front Matter 覆盖)")
191
+ p.add_argument("--recipients", nargs="*", help="主送机关(逗号分隔)")
192
+ p.add_argument("--signer", default="", help="落款单位")
193
+ p.add_argument("--date", default="", help="成文日期")
194
+ p.add_argument("--attachments", nargs="*", help="附件列表")
195
+ p.add_argument("--no-ai-declaration", action="store_true",
196
+ help="P1: 生成文档不追加 AI 声明段(默认追加)")
197
+ p.add_argument("--config-overrides", default="",
198
+ help="规则覆盖 JSON(DSH 插件注入)")
199
+ p.add_argument("--json", action="store_true",
200
+ help="输出结构化 JSON 摘要(Agent 可解析)")
201
+ p.set_defaults(func=cmd_md2docx)
202
+
203
+ # ---- draft:路径 C 一站式生成(Markdown → 国标成品 + 验证)----
204
+ p = sub.add_parser("draft", help="Markdown 草稿 → 国标成品 + 验证(路径 C 四步合一)")
205
+ p.add_argument("input", help="输入 .md 路径,或 '-' 从标准输入读取(支持管道)")
206
+ p.add_argument("-o", "--output", help="输出 .docx 路径(默认 修订版+{类型}-成品+{日期}+v1.docx)")
207
+ p.add_argument("-t", "--doc-type", default="", help="公文类型(默认自动检测/Front Matter,可指定)")
208
+ p.add_argument("--recipients", nargs="*", help="主送机关(逗号分隔)")
209
+ p.add_argument("--signer", default="", help="落款单位")
210
+ p.add_argument("--date", default="", help="成文日期")
211
+ p.add_argument("--attachments", nargs="*", help="附件列表")
212
+ p.add_argument("--no-ai-declaration", action="store_true",
213
+ help="生成的文档不追加 AI 声明段(默认追加)")
214
+ p.add_argument("--verify", dest="verify", action="store_true", default=True,
215
+ help="生成后自动 check 验证(默认开启;P0 存在时退出码非 0)")
216
+ p.add_argument("--no-verify", dest="verify", action="store_false",
217
+ help="跳过生成后验证")
218
+ p.add_argument("--json", action="store_true", help="JSON 结构化输出(Agent 可机器解析)")
219
+ p.add_argument("--config-overrides", default="",
220
+ help="DSH 配置覆盖 JSON 字符串(页边距/行距/字体)")
221
+ p.set_defaults(func=cmd_draft)
222
+
223
+ p = sub.add_parser("optimize-content", help="内容优化差异对比:原文灰色+删除线,修改后红色高亮,每段附修改说明与依据")
224
+ p.add_argument("input", help="输入 .docx 路径")
225
+ p.add_argument("-o", "--output", help="输出 .docx 路径(默认按规范自动命名:{原文档名}+{内容风格}+{日期}+v1.docx)")
226
+ p.add_argument("--changes", required=False, default="",
227
+ help="变更 JSON 文件路径(含 paragraph_index/original_text/optimized_text/reason/reference);"
228
+ "不提供且加 --auto-generate 时基于内置规则自动生成")
229
+ p.add_argument("--optimize-format", action="store_true", help="同时优化格式(默认仅做差异标注,不改格式)")
230
+ p.add_argument("--apply", action="store_true", help="确认生成差异对比文档(默认预览)")
231
+ p.add_argument("--disclaimer", default=None, help="文档末尾 AI 声明文字(默认:内容由GongWen-skill-AI生成,仅供参考)")
232
+ p.add_argument("--force", action="store_true", help="强制替换:文本匹配失败时直接替换段落全部内容(可能丢失加粗等格式)")
233
+ p.add_argument("--paragraphs", type=str, default=None, help='只处理指定段落范围,如 "11-15" 或 "5,7,9"')
234
+ p.add_argument("--comment-mode", action="store_true",
235
+ help="批注模式:将优化建议以 Word 原生批注写入(可审阅→接受/拒绝),而非行内标记")
236
+ p.add_argument("--tracked-change", action="store_true",
237
+ help="修订追踪模式:将修改以 Word 原生修订标记(ins/del)写入,可在审阅面板逐条接受/拒绝")
238
+ p.add_argument("--preset", default="", choices=["quick", "full", "review"],
239
+ help="O6:预设组合——quick 精简快速(3角色+跳过风格增强)/ full 完整默认(6角色+风格增强+事实核验)/ review 完整审稿(6角色+已确认实体批注);显式参数优先于预设")
240
+ p.add_argument("--mode", default="tracked", choices=["inline", "tracked"],
241
+ help="输出模式:tracked 修订+批注(默认,Word 审阅面板逐条接受/拒绝,修改说明写入批注);inline 行内标记(显式降级选择)")
242
+ p.add_argument("--reviewers", type=int, default=argparse.SUPPRESS, choices=[3, 5, 6],
243
+ help="审稿角色数:6 完整版(默认,含事实核验员)/ 5 完整版(历史兼容,同6)/ 3 精简版,意见作为独立批注按审阅者写入")
244
+ p.add_argument("--background", nargs="*", default=None,
245
+ help="背景资料路径(事实核验用,支持多个):.docx / .pdf / .md / .txt / URL,与 --mode tracked 配合对存疑人事信息生成批注提醒")
246
+ p.add_argument("--perspective", default="",
247
+ help="P2 修复:优化视角/风格方向(路径B第0步确认,影响审稿方向和修改说明标注),如 '务实客观,数据驱动,避免主观评价和万能结论'")
248
+ p.add_argument("--show-confirmed", action="store_true",
249
+ help="P7 修复:对已确认实体也生成「✅已确认」批注(默认不生成,避免噪音)")
250
+ p.add_argument("-t", "--doc-type", default="", help="改进 A:显式指定公文类型(默认自动检测)")
251
+ p.add_argument("--show-rules", action="store_true",
252
+ help="改进 A:输出当前文档类型的内容层规则摘要(structure/focus_checks)")
253
+ p.add_argument("--auto-generate", action="store_true",
254
+ help="改进 E:无 --changes 时基于内置规则+风格提示词自动生成优化建议(需配置 GONGWEN_LLM_API)")
255
+ p.add_argument("--output-tasks", default="",
256
+ help="V1:将待 Agent 处理的任务(事实核验实体/风格增强请求)输出到 JSON 文件,同时生成基础版文档")
257
+ p.add_argument("--input-tasks", default="",
258
+ help="V1:读入 Agent 回填的 tasks_result.json,将事实核验修正/风格建议合并到 changes 后执行")
259
+ p.add_argument("--style", default="",
260
+ help="V3:语言风格。不指定则自动推断(changes.style → doc_type 映射 → 默认庄重严谨)")
261
+ p.add_argument("--no-style-enhance", action="store_true",
262
+ help="V3:禁用风格增强步骤(默认启用)")
263
+ p.add_argument("--quiet", action="store_true", help="安静模式:仅输出最终结果,不显示分步进度")
264
+ p.add_argument("--precheck", action="store_true",
265
+ help="O7:预检模式——逐段比对 changes.json 与原文,输出不匹配清单和相似度诊断(不生成文档)")
266
+ p.add_argument("--json", action="store_true",
267
+ help="JSON 输出(与 --precheck 配合输出结构化预检结果)")
268
+ # P1-8 修复:移除从未使用的 --verbose 参数(cmd 函数内无任何引用)
269
+ p.set_defaults(func=cmd_optimize_content)
270
+
271
+ p = sub.add_parser("bold-first", help="正文段落首句加粗(符合公文规范:点题第一句话默认加粗)")
272
+ p.add_argument("input", help="输入 .docx 路径")
273
+ p.add_argument("-o", "--output", help="输出 .docx 路径(默认输入_加粗首句.docx)")
274
+ p.set_defaults(func=cmd_bold_first)
275
+
276
+ p = sub.add_parser("fix-common", help="一键修复常见格式问题(路径D):段落类型修正/编号拆分/首句加粗/加粗范围修复,不含AI声明段")
277
+ p.add_argument("--json", action="store_true", help="JSON 结构化输出(Agent 可机器解析)")
278
+ p.add_argument("input", help="输入 .docx 路径")
279
+ p.add_argument("-o", "--output", help="输出 .docx 路径(默认输入_fix-common.docx)")
280
+ p.set_defaults(func=cmd_fix_common)
281
+
282
+ p = sub.add_parser("handoff", help="查看/写入会话交接文档(跨会话上下文传递,长任务收尾必写)")
283
+ p.add_argument("--list", action="store_true", help="列出所有交接文档摘要")
284
+ p.add_argument("--latest", action="store_true", help="读取最新交接文档(JSON,加 --summary 输出 Markdown 摘要)")
285
+ p.add_argument("--summary", action="store_true", help="以 Markdown 摘要输出(配合 --latest)")
286
+ p.add_argument("--write", metavar="JSON_PATH", help="从 JSON 文件写入交接文档(P2-27)")
287
+ p.set_defaults(func=cmd_handoff)
288
+
289
+ p = sub.add_parser("rule-export", help="导出合并后的规则(YAML/JSON)")
290
+ p.add_argument("type", help="公文类型")
291
+ p.add_argument("-o", "--output", help="输出文件路径")
292
+ p.add_argument("--json", action="store_true", help="以 JSON 格式输出(缺省 YAML)")
293
+ p.set_defaults(func=cmd_rule_export)
294
+
295
+ p = sub.add_parser("rule-list", help="列出三层规则")
296
+ p.add_argument("--source", default="all", choices=["all", "official", "custom", "user"])
297
+ p.add_argument("--json", action="store_true", help="JSON 输出")
298
+ p.set_defaults(func=cmd_rule_list)
299
+
300
+ p = sub.add_parser("rule-import", help="导入/保存自定义规则 YAML")
301
+ p.add_argument("key", help="规则标识符(仅字母数字下划线连字符)")
302
+ p.add_argument("-f", "--file", help="YAML 文件路径")
303
+ p.add_argument("--text", help="YAML 文本内容(内联)")
304
+ p.add_argument("--source", default="user", choices=["user", "custom"], help="保存层级")
305
+ p.set_defaults(func=cmd_rule_import)
306
+
307
+ # ---- 桌签批量生成 ----
308
+ p = sub.add_parser("table-signs", help="从名单批量生成双面桌签(A4纵向,华文新魏,字号按名长动态调整),每人一份或合并多页")
309
+ p.add_argument("input", help="名单文件路径(每行一人,支持逗号/空格/顿号分隔),或 '-' 从标准输入读取")
310
+ p.add_argument("-o", "--output", help="输出目录(默认 ./桌签/;每人单独文件)或输出 .docx 路径(配合 --combined)")
311
+ p.add_argument("--combined", action="store_true", help="合并为一个多页文档(默认每人一个独立文件)")
312
+ p.add_argument('--prefix', default='桌签', help='独立文件时文件名前缀(默认"桌签")')
313
+ # P0-7 修复:--template 改非必填——方案六已内置默认模板(engine/templates/table_sign.dotx),
314
+ # 未传时走默认模板,实现零配置;传了则覆盖
315
+ p.add_argument("--template",
316
+ help="座签模板 .dotx 路径(默认使用内置模板;如 F:/模板/座签模板.dotx)")
317
+ # 方案五(P2-3):占位符参数化
318
+ p.add_argument("--placeholder", default="Jose AI",
319
+ help="座签模板中占位文本(默认 Jose AI)")
320
+ p.set_defaults(func=cmd_table_signs)
321
+
322
+ # ---- 完整审校(路径A + 路径B + 批注) ----
323
+ p = sub.add_parser("full-review", help="完整审校:格式修复(路径A)→ 内容优化(路径B)→ 批注输出,一条命令完成")
324
+ p.add_argument("input", help="输入 .docx 路径")
325
+ p.add_argument("-o", "--output", help="输出 .docx 路径")
326
+ p.add_argument("-t", "--doc-type", default="", help="公文类型(默认自动检测)")
327
+ p.add_argument("--changes", default="", help="变更 JSON 文件路径(路径B优化建议,可省略则仅格式修复+批注空)")
328
+ p.add_argument("--json", action="store_true", help="JSON 结构化输出(Agent 可机器解析)")
329
+ p.set_defaults(func=cmd_full_review)
330
+
331
+ # ---- 样式学习(上传标准文档 → 自定义命名模板) ----
332
+ p = sub.add_parser("style-learn", help="从标准 .docx 文档学习排版样式(含字间距等细微属性),生成自定义命名模板并注册")
333
+ p.add_argument("input", help="输入标准 .docx 文档路径(如单位定稿红头公文)")
334
+ p.add_argument("-n", "--name", default="", help="模板名(默认 自定义_{文档名}),注册后可用 optimize -t {模板名}")
335
+ p.set_defaults(func=cmd_style_learn)
336
+
337
+ p = sub.add_parser("style-list", help="列出所有通过 style-learn 学习的自定义样式模板")
338
+ p.set_defaults(func=cmd_style_list)
339
+
340
+ # ---- 版本自检(PyPI pip 包权威判定,GitHub 备用,GitCode/AtomGit 作国内镜像) ----
341
+ p = sub.add_parser("check-update", help="版本自检:PyPI(pip 包权威)判定更新,GitHub 作备用渠道(GitCode/AtomGit 作国内镜像提示)")
342
+ p.add_argument("--json", action="store_true",
343
+ help="输出 JSON 格式结果(便于 Agent 解析)")
344
+ p.set_defaults(func=cmd_check_update)
345
+
346
+ # ---- 文档审计 ----
347
+ p = sub.add_parser("audit", help="审计文档处理链:检查删除线、加粗、AI声明等合规性问题")
348
+ p.add_argument("input", help="输入 .docx 路径")
349
+ p.set_defaults(func=cmd_audit)
350
+
351
+ # ---- 审稿流转单生成 ----
352
+ p = sub.add_parser("review", help="生成公文审稿流转单(五角色/三角色审核模板)")
353
+ p.add_argument("doc_type", help="公文类型,如 通知/请示/报告/函")
354
+ p.add_argument("-o", "--output", help="输出 .docx 路径(默认 审稿流转单-{type}.docx)")
355
+ p.add_argument("--scheme", default="full", choices=["full", "compact"],
356
+ help="审稿方案:full=完整五角色(默认), compact=精简三角色")
357
+ p.add_argument("--title", default="", help="待审文稿标题")
358
+ p.set_defaults(func=cmd_review)
359
+
360
+ # ---- 向导式交互 ----
361
+ p = sub.add_parser("wizard", help="向导式交互:A/B/C/D/E 路径引导 + 一键执行(--answers 非交互 / --dry-run 只打印)")
362
+ p.add_argument("--answers", default="",
363
+ help="答案 JSON 文件路径(Agent 非交互模式):"
364
+ '{"path":"A","input":"a.docx","apply":true}')
365
+ p.add_argument("--dry-run", action="store_true", help="只打印将执行的命令,不真正执行")
366
+ p.set_defaults(func=cmd_wizard)
367
+
368
+ # ---- 字体管理 ----
369
+ p = sub.add_parser("font", help="公文标准字体管理:安装/检查/列出内置字体")
370
+ p.add_argument("action", nargs="?", default="list",
371
+ choices=["list", "check", "install"],
372
+ help="list=列出字体清单, check=检查安装状态, install=安装字体")
373
+ p.set_defaults(func=cmd_font)
374
+
375
+ # ---- 自我诊断与修复 ----
376
+ p = sub.add_parser("doctor", help="全面诊断:检查 Python 版本/依赖/版本一致性/字体/DSH 文件/代码风格/网络 DNS 等")
377
+ p.add_argument("--json", action="store_true",
378
+ help="输出 JSON 格式结果(便于 Agent 解析)")
379
+ p.add_argument("--offline", action="store_true",
380
+ help="跳过网络/DNS 诊断(离线模式)")
381
+ p.set_defaults(func=cmd_doctor)
382
+
383
+ p = sub.add_parser("repair", help="修复常见问题:安装缺失依赖/字体/同步 SKILL.md 副本")
384
+ p.set_defaults(func=cmd_repair)
385
+
386
+ args = parser.parse_args()
387
+
388
+ if not args.command:
389
+ parser.print_help()
390
+ sys.exit(1)
391
+
392
+ try:
393
+ # P3-22 修复:捕获子命令返回值(如 check-update 的退出码),非零则用于进程退出码
394
+ ret = args.func(args)
395
+ if isinstance(ret, int) and ret != 0:
396
+ sys.exit(ret)
397
+ except FileNotFoundError as e:
398
+ print(f"错误:文件不存在 - {e}", file=sys.stderr)
399
+ sys.exit(1)
400
+ except Exception as e:
401
+ print(f"错误:{e}", file=sys.stderr)
402
+ sys.exit(1)
403
+
404
+
405
+ if __name__ == "__main__":
406
+ main()
@@ -25,12 +25,15 @@ from __future__ import annotations
25
25
  import argparse
26
26
  import contextlib
27
27
  import json
28
+ import logging
28
29
  import shutil
29
30
  import sys
30
31
  import tempfile
31
32
  from datetime import date as _dt
32
33
  from pathlib import Path
33
34
 
35
+ _logger = logging.getLogger(__name__)
36
+
34
37
 
35
38
  def _resolve_fm_doc_type(input_src: str, explicit: str | None) -> str:
36
39
  """从 Markdown Front Matter 提取 doc_type(仅文件路径时;'-' 或显式 -t 直接返回)。
@@ -134,8 +137,8 @@ def cmd_draft(args: argparse.Namespace) -> int:
134
137
  try:
135
138
  import json as _json
136
139
  _opt_json = _json.loads(_opt_out)
137
- except Exception:
138
- pass
140
+ except Exception as e:
141
+ _logger.warning("draft: 解析 optimize --json 输出失败(子结果降级为空): %s", e)
139
142
  else:
140
143
  _opt_out = ""
141
144
  _opt_json = None
@@ -116,8 +116,8 @@ def _is_font_installed(font_name: str) -> bool:
116
116
  except OSError:
117
117
  break
118
118
  winreg.CloseKey(key)
119
- except Exception:
120
- pass
119
+ except Exception as e:
120
+ _logger.debug("查询系统字体注册表失败: %s", e)
121
121
  return False
122
122
  else:
123
123
  # Linux/macOS: 检查常见字体目录
@@ -165,8 +165,8 @@ def _install_font_file(font_path: Path, font_name: str) -> bool:
165
165
  reg_name = f"{font_name} (TrueType)"
166
166
  winreg.SetValueEx(key, reg_name, 0, winreg.REG_SZ, dest.name)
167
167
  winreg.CloseKey(key)
168
- except Exception:
169
- pass
168
+ except Exception as e:
169
+ _logger.debug("注册表指向已存在字体失败(跳过注册,不影响复制): %s", e)
170
170
  print(f" ⏭️ {font_name}: 已存在(跳过)")
171
171
  return True
172
172
 
@@ -212,8 +212,8 @@ def _install_font_file(font_path: Path, font_name: str) -> bool:
212
212
  import subprocess
213
213
  subprocess.run(["fc-cache", "-f", str(fonts_dir)],
214
214
  capture_output=True, timeout=30)
215
- except Exception:
216
- pass
215
+ except Exception as e:
216
+ _logger.debug("刷新 fc-cache 失败(字体已复制,重启应用后生效): %s", e)
217
217
  print(f" ✅ {font_name}: 已安装 → {dest}")
218
218
  return True
219
219
 
@@ -20,12 +20,15 @@ _logger = logging.getLogger(__name__)
20
20
 
21
21
 
22
22
  def cmd_rule_export(args):
23
- """导出某类型的合并规则为 YAML。"""
23
+ """导出某类型的合并规则(默认 YAML,--json 输出 JSON)。"""
24
24
  from engine.core.rules.manager import load_rules_merged
25
25
  import yaml
26
26
 
27
27
  rules = load_rules_merged(args.type)
28
- text = yaml.dump(rules, allow_unicode=True, default_flow_style=False, sort_keys=False)
28
+ if getattr(args, "json", False):
29
+ text = json.dumps(rules, ensure_ascii=False, indent=2)
30
+ else:
31
+ text = yaml.dump(rules, allow_unicode=True, default_flow_style=False, sort_keys=False)
29
32
  if args.output:
30
33
  Path(args.output).write_text(text, encoding="utf-8")
31
34
  print(f"规则已导出: {args.output} (类型: {args.type})")
@@ -22,11 +22,14 @@ from __future__ import annotations
22
22
 
23
23
  import argparse
24
24
  import json
25
+ import logging
25
26
  import shlex
26
27
  import subprocess
27
28
  import sys
28
29
  from pathlib import Path
29
30
 
31
+ _logger = logging.getLogger(__name__)
32
+
30
33
  # 路径定义:(键, 标题, 一句话说明, 子命令)
31
34
  PATH_DEFS = [
32
35
  ("A", "格式优化", "不改文字只修排版,按国标(GB/T 9704)标准化", "optimize"),
@@ -127,7 +130,7 @@ def _resolve_doc_type(raw: str, types: list[str]) -> str:
127
130
  if raw == kw or raw in kw or kw in raw:
128
131
  return tid
129
132
  except ImportError:
130
- pass
133
+ _logger.debug("TYPE_KEYWORDS 导入失败,回退子串匹配(不影响主流程)")
131
134
 
132
135
  # 子串匹配 id(如 "not" → notice)
133
136
  hits = [t for t in types if raw.lower() in t]
@@ -22,6 +22,7 @@
22
22
  """
23
23
  from __future__ import annotations
24
24
 
25
+ import logging
25
26
  import re
26
27
  from typing import Any
27
28
 
@@ -33,6 +34,8 @@ from docx.shared import Mm, Cm, Pt, RGBColor
33
34
 
34
35
  from engine.core.document.font_utils import set_run_font, BODY_FONT, LATIN_FONT
35
36
 
37
+ _logger = logging.getLogger(__name__)
38
+
36
39
  # V2.3:导语/过渡词开头 → 不是主送机关/称呼段(与 modifier.detect_paragraph_type 的
37
40
  # _INTRODUCTION_RE/_TRANSITION_RE 逻辑对齐,避免"为深入贯彻落实…通知如下:"短导语被
38
41
  # 误判为称呼段而丢失首行缩进)
@@ -147,9 +150,9 @@ def _apply_doc_defaults(doc: Document, body_pt: float = 16.0) -> None:
147
150
  szCs = OxmlElement("w:szCs")
148
151
  rPr.append(szCs)
149
152
  szCs.set(qn("w:val"), str(int(body_pt * 2)))
150
- except Exception:
153
+ except Exception as e:
151
154
  # 失败不影响主流程,仍按逐段显式格式输出
152
- pass
155
+ _logger.debug("设置段落默认字体失败,按逐段显式格式输出: %s", e)
153
156
 
154
157
 
155
158
  def _apply_section(doc: Document, page_setup) -> None:
@@ -404,8 +407,8 @@ def _add_paragraph(doc: Document, para, rules: dict):
404
407
  try:
405
408
  rgb = str(fmt.color).lstrip("#")
406
409
  run.font.color.rgb = RGBColor(int(rgb[0:2], 16), int(rgb[2:4], 16), int(rgb[4:6], 16))
407
- except Exception:
408
- pass
410
+ except Exception as e:
411
+ _logger.debug("颜色解析失败,跳过该 run 的字体颜色: %s", e)
409
412
  return p
410
413
 
411
414
 
@@ -582,7 +585,7 @@ def render_model_to_docx(model, output_path, rules: dict | None = None,
582
585
  # 统一文档作者(用户要求:生成文档的作者写为 Jose AI)
583
586
  try:
584
587
  doc.core_properties.author = "Jose AI"
585
- except Exception:
586
- pass
588
+ except Exception as e:
589
+ _logger.debug("设置文档作者失败: %s", e)
587
590
  doc.save(out)
588
591
  return output_path
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gongwen-skill",
3
- "version": "2.9.0",
3
+ "version": "2.10.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.9.0
204
+ Skill 版本: v2.10.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.9.0"
7
+ version = "2.10.0"
8
8
  description = "公文全流程处理工具 - GB/T 9704 格式检查/修复/内容优化/模板生成/版式注入"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}