@hupan56/wlkj 2.7.12 → 3.1.2

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.
Files changed (253) hide show
  1. package/bin/cli.js +1435 -1107
  2. package/package.json +29 -29
  3. package/templates/qoder/agents/insight-planning.md +1 -1
  4. package/templates/qoder/agents/insight-research.md +28 -15
  5. package/templates/qoder/commands/optional/wl-insight.md +161 -161
  6. package/templates/qoder/commands/optional/wl-report.md +20 -39
  7. package/templates/qoder/commands/optional/wl-status.md +4 -4
  8. package/templates/qoder/commands/wl-code.md +2 -2
  9. package/templates/qoder/commands/wl-design.md +6 -6
  10. package/templates/qoder/commands/wl-init.md +3 -3
  11. package/templates/qoder/commands/wl-prd.md +26 -13
  12. package/templates/qoder/commands/wl-req.md +43 -0
  13. package/templates/qoder/commands/wl-search.md +78 -47
  14. package/templates/qoder/commands/wl-task.md +343 -40
  15. package/templates/qoder/commands/wl-test.md +43 -17
  16. package/templates/qoder/config.yaml +52 -14
  17. package/templates/qoder/hooks/post-tool-use.py +181 -0
  18. package/templates/qoder/hooks/session-start.py +94 -23
  19. package/templates/qoder/hooks/stop-eval.py +207 -0
  20. package/templates/qoder/hooks/user-prompt-submit.py +139 -0
  21. package/templates/qoder/rules/wl-pipeline.md +85 -49
  22. package/templates/qoder/scripts/README.md +102 -0
  23. package/templates/qoder/scripts/capability/__init__.py +26 -0
  24. package/templates/qoder/scripts/capability/__main__.py +72 -0
  25. package/templates/qoder/scripts/capability/adapters/__init__.py +29 -0
  26. package/templates/qoder/scripts/capability/adapters/cli.py +316 -0
  27. package/templates/qoder/scripts/capability/adapters/mcp.py +334 -0
  28. package/templates/qoder/scripts/capability/adapters/qw.py +271 -0
  29. package/templates/qoder/scripts/capability/caps/__init__.py +27 -0
  30. package/templates/qoder/scripts/capability/caps/context.py +36 -0
  31. package/templates/qoder/scripts/capability/caps/cron.py +50 -0
  32. package/templates/qoder/scripts/capability/caps/identity.py +43 -0
  33. package/templates/qoder/scripts/capability/caps/memory.py +71 -0
  34. package/templates/qoder/scripts/capability/caps/notify.py +41 -0
  35. package/templates/qoder/scripts/capability/caps/present.py +48 -0
  36. package/templates/qoder/scripts/capability/caps/repo.py +31 -0
  37. package/templates/qoder/scripts/capability/caps/sandbox.py +43 -0
  38. package/templates/qoder/scripts/capability/chain.py +92 -0
  39. package/templates/qoder/scripts/capability/memory_chain.py +41 -0
  40. package/templates/qoder/scripts/capability/registry.py +246 -0
  41. package/templates/qoder/scripts/capability/registry_mcp.py +250 -0
  42. package/templates/qoder/scripts/capability/smoke_test.py +211 -0
  43. package/templates/qoder/scripts/capability/smoke_test_report.json +94 -0
  44. package/templates/qoder/scripts/deployment/setup/__init__.py +11 -0
  45. package/templates/qoder/scripts/deployment/setup/carriers.py +669 -0
  46. package/templates/qoder/scripts/deployment/setup/carriers_verify.py +148 -0
  47. package/templates/qoder/scripts/{init_doctor.py → deployment/setup/init_doctor.py} +100 -39
  48. package/templates/qoder/scripts/{install_qoderwork.py → deployment/setup/install_qoderwork.py} +48 -30
  49. package/templates/qoder/scripts/{platform_doctor.py → deployment/setup/platform_doctor.py} +271 -259
  50. package/templates/qoder/scripts/{repo_root.py → deployment/setup/repo_root.py} +9 -2
  51. package/templates/qoder/scripts/{setup.py → deployment/setup/setup.py} +143 -14
  52. package/templates/qoder/scripts/{setup_lanhu.py → deployment/setup/setup_lanhu.py} +979 -963
  53. package/templates/qoder/scripts/domain/design/__init__.py +0 -0
  54. package/templates/qoder/scripts/{fill_prototype.py → domain/design/fill_prototype.py} +17 -5
  55. package/templates/qoder/scripts/{gen_design_doc.py → domain/design/gen_design_doc.py} +406 -394
  56. package/templates/qoder/scripts/domain/kg/__init__.py +11 -0
  57. package/templates/qoder/scripts/domain/kg/build/__init__.py +0 -0
  58. package/templates/qoder/scripts/domain/kg/build/build_entity_registry.py +201 -0
  59. package/templates/qoder/scripts/domain/kg/build/build_relations.py +132 -0
  60. package/templates/qoder/scripts/{build_style_index.py → domain/kg/build/build_style_index.py} +50 -15
  61. package/templates/qoder/scripts/domain/kg/build/build_workflows.py +149 -0
  62. package/templates/qoder/scripts/{kg_build.py → domain/kg/build/kg_build.py} +685 -612
  63. package/templates/qoder/scripts/{kg_build_db.py → domain/kg/build/kg_build_db.py} +338 -327
  64. package/templates/qoder/scripts/{kg_incremental.py → domain/kg/build/kg_incremental.py} +425 -393
  65. package/templates/qoder/scripts/{learn_aggregate.py → domain/kg/build/learn_aggregate.py} +212 -201
  66. package/templates/qoder/scripts/domain/kg/extract/__init__.py +0 -0
  67. package/templates/qoder/scripts/{common → domain/kg/extract}/extract.py +430 -419
  68. package/templates/qoder/scripts/domain/kg/extract/test_extract.py +115 -0
  69. package/templates/qoder/scripts/{common → domain/kg/extract}/ts_extract.py +614 -536
  70. package/templates/qoder/scripts/domain/kg/graph/__init__.py +0 -0
  71. package/templates/qoder/scripts/{kg_link_db.py → domain/kg/graph/kg_link_db.py} +235 -224
  72. package/templates/qoder/scripts/domain/kg/graph/kg_semantic.py +683 -0
  73. package/templates/qoder/scripts/{kg.py → domain/kg/kg.py} +871 -708
  74. package/templates/qoder/scripts/domain/kg/kg_capabilities.py +182 -0
  75. package/templates/qoder/scripts/domain/kg/search/__init__.py +0 -0
  76. package/templates/qoder/scripts/{context_pack.py → domain/kg/search/context_pack.py} +45 -17
  77. package/templates/qoder/scripts/{enrich_prompt.py → domain/kg/search/enrich_prompt.py} +238 -226
  78. package/templates/qoder/scripts/domain/kg/search/prefetch.py +384 -0
  79. package/templates/qoder/scripts/{common → domain/kg/search}/search_engine.py +207 -205
  80. package/templates/qoder/scripts/{search_index.py → domain/kg/search/search_index.py} +210 -88
  81. package/templates/qoder/scripts/domain/kg/server/__init__.py +0 -0
  82. package/templates/qoder/scripts/domain/kg/server/kgd.py +549 -0
  83. package/templates/qoder/scripts/domain/kg/server/perf_bench.py +197 -0
  84. package/templates/qoder/scripts/domain/kg/storage/__init__.py +0 -0
  85. package/templates/qoder/scripts/{kg_duckdb.py → domain/kg/storage/kg_duckdb.py} +70 -45
  86. package/templates/qoder/scripts/domain/learning/__init__.py +0 -0
  87. package/templates/qoder/scripts/{learn.py → domain/learning/learn.py} +157 -146
  88. package/templates/qoder/scripts/domain/report/__init__.py +11 -0
  89. package/templates/qoder/scripts/{add_session.py → domain/report/add_session.py} +256 -244
  90. package/templates/qoder/scripts/{export.py → domain/report/export.py} +72 -4
  91. package/templates/qoder/scripts/{report.py → domain/report/report.py} +292 -281
  92. package/templates/qoder/scripts/domain/report/report_snapshot.py +261 -0
  93. package/templates/qoder/scripts/domain/report/role.py +39 -0
  94. package/templates/qoder/scripts/{status.py → domain/report/status.py} +737 -628
  95. package/templates/qoder/scripts/domain/report/status_snapshot.py +191 -0
  96. package/templates/qoder/scripts/domain/requirement/__init__.py +0 -0
  97. package/templates/qoder/scripts/{archive_prd.py → domain/requirement/archive_prd.py} +389 -377
  98. package/templates/qoder/scripts/domain/requirement/req.py +228 -0
  99. package/templates/qoder/scripts/domain/task/__init__.py +11 -0
  100. package/templates/qoder/scripts/{git_sync.py → domain/task/git_sync.py} +90 -50
  101. package/templates/qoder/scripts/{syncgate.py → domain/task/syncgate.py} +18 -7
  102. package/templates/qoder/scripts/domain/task/task.py +229 -0
  103. package/templates/qoder/scripts/domain/task/task_lifecycle.py +606 -0
  104. package/templates/qoder/scripts/domain/task/task_query.py +162 -0
  105. package/templates/qoder/scripts/domain/task/task_relations.py +425 -0
  106. package/templates/qoder/scripts/{team_sync.py → domain/task/team_sync.py} +101 -23
  107. package/templates/qoder/scripts/domain/task/zentao_sync.py +370 -0
  108. package/templates/qoder/scripts/foundation/__init__.py +0 -0
  109. package/templates/qoder/scripts/foundation/bootstrap.py +145 -0
  110. package/templates/qoder/scripts/foundation/core/__init__.py +0 -0
  111. package/templates/qoder/scripts/foundation/core/cmd_registry.py +112 -0
  112. package/templates/qoder/scripts/foundation/core/config.py +187 -0
  113. package/templates/qoder/scripts/{common → foundation/core}/paths.py +706 -400
  114. package/templates/qoder/scripts/foundation/data/__init__.py +0 -0
  115. package/templates/qoder/scripts/foundation/data/contract.py +317 -0
  116. package/templates/qoder/scripts/{common → foundation/data}/events.py +2 -2
  117. package/templates/qoder/scripts/foundation/data/result.py +223 -0
  118. package/templates/qoder/scripts/{common → foundation/data}/task_utils.py +427 -392
  119. package/templates/qoder/scripts/foundation/identity/__init__.py +0 -0
  120. package/templates/qoder/scripts/foundation/identity/check_publish.py +103 -0
  121. package/templates/qoder/scripts/{common → foundation/identity}/developer.py +239 -238
  122. package/templates/qoder/scripts/foundation/identity/guard.py +159 -0
  123. package/templates/qoder/scripts/{common → foundation/identity}/identity.py +132 -9
  124. package/templates/qoder/scripts/foundation/identity/roles.py +60 -0
  125. package/templates/qoder/scripts/foundation/integrations/__init__.py +0 -0
  126. package/templates/qoder/scripts/{common → foundation/integrations}/active_task.py +30 -15
  127. package/templates/qoder/scripts/{common → foundation/integrations}/feishu.py +11 -10
  128. package/templates/qoder/scripts/{common → foundation/integrations}/terms.py +3 -3
  129. package/templates/qoder/scripts/foundation/integrations/zentao_client.py +220 -0
  130. package/templates/qoder/scripts/foundation/io/__init__.py +0 -0
  131. package/templates/qoder/scripts/{common → foundation/io}/filelock.py +1 -1
  132. package/templates/qoder/scripts/{common → foundation/io}/reqid.py +1 -1
  133. package/templates/qoder/scripts/foundation/protocol/__init__.py +0 -0
  134. package/templates/qoder/scripts/foundation/protocol/duckdb_conn.py +39 -0
  135. package/templates/qoder/scripts/foundation/protocol/mcp_base.py +268 -0
  136. package/templates/qoder/scripts/foundation/utils/__init__.py +0 -0
  137. package/templates/qoder/scripts/{common → foundation/utils}/eval_api.py +12 -1
  138. package/templates/qoder/scripts/{common → foundation/utils}/pip_install.py +23 -6
  139. package/templates/qoder/scripts/{common → foundation/utils}/platform_guard.py +1 -1
  140. package/templates/qoder/scripts/orchestration/__init__.py +0 -0
  141. package/templates/qoder/scripts/orchestration/wlkj.py +185 -0
  142. package/templates/qoder/scripts/protocol/__init__.py +0 -0
  143. package/templates/qoder/scripts/protocol/browser/README.md +207 -0
  144. package/templates/qoder/scripts/protocol/browser/SKILL.md +265 -0
  145. package/templates/qoder/scripts/protocol/browser/config.env +9 -0
  146. package/templates/qoder/scripts/protocol/browser/references/cdp-api.md +110 -0
  147. package/templates/qoder/scripts/protocol/browser/references/migration-2.5.3.md +72 -0
  148. package/templates/qoder/scripts/protocol/browser/references/site-patterns/.gitkeep +0 -0
  149. package/templates/qoder/scripts/protocol/browser/scripts/browser-discovery.mjs +138 -0
  150. package/templates/qoder/scripts/protocol/browser/scripts/cdp-proxy.mjs +672 -0
  151. package/templates/qoder/scripts/protocol/browser/scripts/check-deps.mjs +206 -0
  152. package/templates/qoder/scripts/protocol/browser/scripts/find-url.mjs +253 -0
  153. package/templates/qoder/scripts/protocol/browser/scripts/match-site.mjs +46 -0
  154. package/templates/qoder/scripts/protocol/browser/templates/config.env.template +9 -0
  155. package/templates/qoder/scripts/protocol/mcp/__init__.py +11 -0
  156. package/templates/qoder/scripts/protocol/mcp/kg_mcp_server.py +478 -0
  157. package/templates/qoder/scripts/{lanhu_stdio_wrapper.py → protocol/mcp/lanhu_stdio_wrapper.py} +131 -119
  158. package/templates/qoder/scripts/protocol/mcp/mcp_doctor.py +548 -0
  159. package/templates/qoder/scripts/protocol/mcp/mcp_launcher.py +600 -0
  160. package/templates/qoder/scripts/protocol/mcp/mysql_mcp_server.py +461 -0
  161. package/templates/qoder/scripts/protocol/mcp/zentao_mcp_server.py +1920 -0
  162. package/templates/qoder/scripts/protocol/transports/__init__.py +56 -0
  163. package/templates/qoder/scripts/protocol/transports/base.py +87 -0
  164. package/templates/qoder/scripts/protocol/transports/cli.py +132 -0
  165. package/templates/qoder/scripts/protocol/transports/http.py +141 -0
  166. package/templates/qoder/scripts/protocol/transports/stdio.py +293 -0
  167. package/templates/qoder/scripts/run_weekly_update.bat +27 -18
  168. package/templates/qoder/scripts/run_weekly_update.sh +22 -13
  169. package/templates/qoder/scripts/validation/__init__.py +0 -0
  170. package/templates/qoder/scripts/validation/eval/__init__.py +0 -0
  171. package/templates/qoder/scripts/validation/eval/perf-report-2026-06-24.md +139 -0
  172. package/templates/qoder/scripts/validation/eval/qwork_harness.py +617 -0
  173. package/templates/qoder/scripts/validation/eval/report-commands.md +220 -0
  174. package/templates/qoder/scripts/validation/eval/transcript_timing.py +386 -0
  175. package/templates/qoder/scripts/validation/metrics/__init__.py +0 -0
  176. package/templates/qoder/scripts/{eval_prd.py → validation/metrics/eval_prd.py} +84 -15
  177. package/templates/qoder/scripts/validation/metrics/usability_score.py +750 -0
  178. package/templates/qoder/scripts/validation/test/__init__.py +11 -0
  179. package/templates/qoder/scripts/validation/test/assertion_gen.py +551 -0
  180. package/templates/qoder/scripts/{autotest.py → validation/test/autotest.py} +1234 -1751
  181. package/templates/qoder/scripts/validation/test/autotest_auth.py +109 -0
  182. package/templates/qoder/scripts/{autotest_batch.py → validation/test/autotest_batch.py} +255 -224
  183. package/templates/qoder/scripts/validation/test/autotest_data.py +680 -0
  184. package/templates/qoder/scripts/validation/test/autotest_opencli.py +646 -0
  185. package/templates/qoder/scripts/{autotest_run.py → validation/test/autotest_run.py} +323 -297
  186. package/templates/qoder/scripts/validation/test/autotest_webaccess.py +571 -0
  187. package/templates/qoder/scripts/{benchmark.py → validation/test/benchmark.py} +17 -5
  188. package/templates/qoder/scripts/{kg_auto_login.py → validation/test/kg_auto_login.py} +208 -196
  189. package/templates/qoder/scripts/{kg_test_runner.py → validation/test/kg_test_runner.py} +13 -2
  190. package/templates/qoder/scripts/{page_probe.py → validation/test/page_probe.py} +470 -459
  191. package/templates/qoder/scripts/validation/test/smoke_all_commands.py +170 -0
  192. package/templates/qoder/settings.json +23 -2
  193. package/templates/qoder/skills/design-import/SKILL.md +235 -226
  194. package/templates/qoder/skills/design-review/SKILL.md +91 -82
  195. package/templates/qoder/skills/prd-generator/SKILL.md +38 -24
  196. package/templates/qoder/skills/prd-review/SKILL.md +8 -6
  197. package/templates/qoder/skills/prototype-generator/SKILL.md +296 -247
  198. package/templates/qoder/skills/spec-coder/SKILL.md +24 -15
  199. package/templates/qoder/skills/spec-generator/SKILL.md +24 -12
  200. package/templates/qoder/skills/test-generator/SKILL.md +9 -7
  201. package/templates/qoder/skills/wl-code/SKILL.md +25 -13
  202. package/templates/qoder/skills/wl-commit/SKILL.md +10 -9
  203. package/templates/qoder/skills/wl-design/SKILL.md +7 -5
  204. package/templates/qoder/skills/wl-init/SKILL.md +8 -8
  205. package/templates/qoder/skills/wl-insight/SKILL.md +25 -12
  206. package/templates/qoder/skills/wl-prd-full/SKILL.md +59 -8
  207. package/templates/qoder/skills/wl-prd-quick/SKILL.md +7 -7
  208. package/templates/qoder/skills/wl-prd-review/SKILL.md +18 -6
  209. package/templates/qoder/skills/wl-report/SKILL.md +27 -25
  210. package/templates/qoder/skills/wl-search/SKILL.md +151 -80
  211. package/templates/qoder/skills/wl-spec/SKILL.md +9 -7
  212. package/templates/qoder/skills/wl-status/SKILL.md +50 -18
  213. package/templates/qoder/skills/wl-task/SKILL.md +94 -12
  214. package/templates/qoder/skills/wl-test/SKILL.md +154 -45
  215. package/templates/qoder/templates/prd-full-template.md +7 -0
  216. package/templates/root/AGENTS.md +275 -237
  217. package/templates/root/requirements.txt +3 -0
  218. package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +3 -3
  219. package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +2 -2
  220. package/templates/qoder/hooks/inject-workflow-state.py +0 -169
  221. package/templates/qoder/scripts/__pycache__/check_mcp_launch.cpython-39.pyc +0 -0
  222. package/templates/qoder/scripts/__pycache__/install_qoderwork.cpython-39.pyc +0 -0
  223. package/templates/qoder/scripts/__pycache__/mcp_launcher.cpython-39.pyc +0 -0
  224. package/templates/qoder/scripts/__pycache__/platform_doctor.cpython-39.pyc +0 -0
  225. package/templates/qoder/scripts/check_carriers.py +0 -238
  226. package/templates/qoder/scripts/check_mcp.py +0 -298
  227. package/templates/qoder/scripts/check_mcp_launch.py +0 -183
  228. package/templates/qoder/scripts/check_qoderwork_consistency.py +0 -166
  229. package/templates/qoder/scripts/collect_prds.py +0 -31
  230. package/templates/qoder/scripts/common/mentions.py +0 -134
  231. package/templates/qoder/scripts/common/utf8.py +0 -38
  232. package/templates/qoder/scripts/extract_api_params.py +0 -246
  233. package/templates/qoder/scripts/extract_routes.py +0 -54
  234. package/templates/qoder/scripts/extract_routes_tree.py +0 -78
  235. package/templates/qoder/scripts/handoff.py +0 -22
  236. package/templates/qoder/scripts/init_developer.py +0 -76
  237. package/templates/qoder/scripts/kg_mcp_server.py +0 -801
  238. package/templates/qoder/scripts/kg_semantic.py +0 -150
  239. package/templates/qoder/scripts/mcp_launcher.py +0 -414
  240. package/templates/qoder/scripts/mysql_mcp_server.py +0 -396
  241. package/templates/qoder/scripts/parse_prds.py +0 -33
  242. package/templates/qoder/scripts/role.py +0 -51
  243. package/templates/qoder/scripts/sync_carriers.py +0 -259
  244. package/templates/qoder/scripts/task.py +0 -1261
  245. package/templates/qoder/scripts/workspace_init.py +0 -102
  246. package/templates/qoder/scripts/zentao_mcp_server.py +0 -424
  247. package/templates/qoder/skills/prompt-enrich/SKILL.md +0 -90
  248. /package/templates/qoder/scripts/{common → deployment}/__init__.py +0 -0
  249. /package/templates/qoder/{skills/prototype-generator/SKILL.md.zcode-79180-2af4721f-f9a6-412c-88db-c0af680d211b.tmp → scripts/domain/__init__.py} +0 -0
  250. /package/templates/qoder/scripts/{common → domain/kg/graph}/graph_traverse.py +0 -0
  251. /package/templates/qoder/scripts/{common → domain/kg/server}/repowiki.py +0 -0
  252. /package/templates/qoder/scripts/{common → foundation/io}/atomicio.py +0 -0
  253. /package/templates/qoder/scripts/{secure-ls.js → validation/test/secure-ls.js} +0 -0
@@ -1,259 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- sync_carriers.py - 四载体 description 自动同步 (skill -> command)
5
-
6
- 为什么需要它:
7
- check_carriers.py 能发现漂移, 但修复一直靠手工。git status 里
8
- sync_templates.py 长期 M 状态就是手工同步的证据。
9
- 本脚本把权威源 (skill 的 SKILL.md frontmatter) 的 description 自动同步到
10
- command 文件, 让"手工对齐"变成"一条命令"。
11
-
12
- 权威源 = skill (skills/wl-X/SKILL.md):
13
- - skill 更新最频繁 (QoderWork 直接读, 迭代快)
14
- - skill 的 description 最全 (含中文 + 英文 + 触发词)
15
- - command 的 description 往往是旧版/简化版
16
-
17
- 同步规则:
18
- - 只同步 description 字段 (其他字段 name/argument-hint/auto-approve/allowed-tools 保留)
19
- - skill description 可能很长 (含触发词), 但 command 的 description 该简洁
20
- → 截取到第一个"。用户说"之前的部分 (核心功能描述), 去掉冗长的触发词列举
21
- - 幂等: 已一致则跳过
22
- - 支持 --dry-run 预览
23
-
24
- Usage:
25
- python .qoder/scripts/sync_carriers.py # 同步所有漂移的
26
- python .qoder/scripts/sync_carriers.py --dry-run # 只预览不改
27
- python .qoder/scripts/sync_carriers.py wl-search # 只同步指定命令
28
- Exit: 0 = 全部同步完成 (或已一致), 1 = 出错
29
- """
30
- import argparse
31
- import os
32
- import re
33
- import sys
34
-
35
- if sys.platform == 'win32':
36
- try:
37
- sys.stdout.reconfigure(encoding='utf-8')
38
- except (AttributeError, IOError):
39
- pass
40
-
41
- sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
42
- from common.paths import get_repo_root
43
-
44
- BASE = get_repo_root()
45
- COMMANDS_DIR = BASE / '.qoder' / 'commands'
46
- SKILLS_DIR = BASE / '.qoder' / 'skills'
47
-
48
-
49
- def _read_frontmatter(path):
50
- """读 .md 的 frontmatter, 返回 (fm_dict, body, raw_fm_text)。
51
- raw_fm_text 用于精确替换 (保留原始格式)。
52
- """
53
- text = path.read_text(encoding='utf-8', errors='ignore')
54
- if not text.startswith('---'):
55
- return {}, text, ''
56
- parts = text.split('---', 2)
57
- if len(parts) < 3:
58
- return {}, text, ''
59
- fm_raw = parts[1]
60
- body = parts[2]
61
- return _parse_fm(fm_raw), body, fm_raw
62
-
63
-
64
- def _parse_fm(fm_text):
65
- """解析 YAML frontmatter (简化版, 不引外部库)。返回 dict。
66
- 处理带引号的值和裸值。
67
- """
68
- fm = {}
69
- for line in fm_text.splitlines():
70
- line = line.rstrip()
71
- if not line.strip() or line.strip().startswith('#'):
72
- continue
73
- if ':' not in line:
74
- continue
75
- key, _, val = line.partition(':')
76
- key = key.strip()
77
- val = val.strip()
78
- # 去引号
79
- if (val.startswith('"') and val.endswith('"')) or \
80
- (val.startswith("'") and val.endswith("'")):
81
- val = val[1:-1]
82
- fm[key] = val
83
- return fm
84
-
85
-
86
- def _condense_description(desc):
87
- """skill 的 description 往往很长 (含触发词列举), command 的该简洁。
88
- 截取策略: 保留到"用户说"或"触发"或第一个句号之前的核心描述。
89
- 保留中英双语部分 (英文描述通常是核心)。
90
- """
91
- if not desc:
92
- return desc
93
- # 找截断点: "用户说" / "触发" / "时触发"
94
- for marker in ['。用户说', ';用户说', ' 用户说', '。触发:', ' 触发:', ';触发', '。时触发', ' 时触发']:
95
- idx = desc.find(marker)
96
- if idx > 0:
97
- return desc[:idx + 1] # 含句号
98
- # 没找到截断点 → 原样返回 (可能本就简洁)
99
- return desc
100
-
101
-
102
- def _find_command_file(name):
103
- """在 commands/ 和 commands/optional/ 下找命令文件。返回 Path 或 None。"""
104
- for cand in [COMMANDS_DIR / (name + '.md'),
105
- COMMANDS_DIR / 'optional' / (name + '.md')]:
106
- if cand.is_file():
107
- return cand
108
- return None
109
-
110
-
111
- def _find_skill_file(name):
112
- """skills/wl-X/SKILL.md。"""
113
- p = SKILLS_DIR / name / 'SKILL.md'
114
- return p if p.is_file() else None
115
-
116
-
117
- def sync_one(name, dry_run=False):
118
- """同步一个命令。返回 ('synced'/'skipped'/'error', 详情)。"""
119
- skill_path = _find_skill_file(name)
120
- cmd_path = _find_command_file(name)
121
- if not skill_path:
122
- return 'error', 'skill 不存在: %s' % skill_path
123
- if not cmd_path:
124
- return 'error', 'command 不存在: %s' % cmd_path
125
-
126
- skill_fm, _, _ = _read_frontmatter(skill_path)
127
- skill_desc = skill_fm.get('description', '')
128
- if not skill_desc:
129
- return 'skipped', 'skill 无 description 字段'
130
-
131
- cmd_fm, cmd_body, cmd_fm_raw = _read_frontmatter(cmd_path)
132
- cmd_desc = cmd_fm.get('description', '')
133
-
134
- # 精简 skill desc (command 不该塞一堆触发词)
135
- condensed = _condense_description(skill_desc)
136
-
137
- if cmd_desc == condensed:
138
- return 'skipped', '已一致'
139
-
140
- # dry-run 只预览
141
- if dry_run:
142
- return 'would-sync', ' 旧: %s\n 新: %s' % (cmd_desc[:70], condensed[:70])
143
-
144
- # 精确替换 frontmatter 里的 description 行
145
- new_fm = _replace_desc_in_fm(cmd_fm_raw, condensed)
146
- if new_fm is None:
147
- return 'error', '无法解析 command frontmatter 的 description 行'
148
-
149
- new_text = '---' + new_fm + '---' + cmd_body
150
- cmd_path.write_text(new_text, encoding='utf-8')
151
- return 'synced', condensed[:70]
152
-
153
-
154
- def _replace_desc_in_fm(fm_raw, new_desc):
155
- """在 frontmatter 原文里替换 description 行。保留其他行原样。
156
- 返回新的 fm_raw 或 None (无法替换)。
157
- """
158
- # 匹配 description: 开头的行 (可能带引号)
159
- # 用 json.dumps 保证引号转义正确 (description 含特殊字符)
160
- escaped = _yaml_escape(new_desc)
161
- pat = re.compile(r'^(\s*description:\s*)(.*)$', re.MULTILINE)
162
- new_fm, n = pat.subn(lambda m: m.group(1) + escaped, fm_raw, count=1)
163
- if n == 0:
164
- # 没有 description 行 → 插一行 (在 name: 之后)
165
- if re.search(r'^\s*name:', fm_raw, re.MULTILINE):
166
- new_fm = re.sub(
167
- r'^(\s*name:\s*.*)$',
168
- r'\1\ndescription: ' + escaped,
169
- fm_raw, count=1, flags=re.MULTILINE)
170
- else:
171
- return None
172
- return new_fm
173
-
174
-
175
- def _yaml_escape(s):
176
- """YAML 字符串转义: 含特殊字符(: # 等)用双引号包裹, 内部双引号转义。
177
- 简化: 一律用双引号 + JSON 转义 (最安全)。
178
- """
179
- import json
180
- return json.dumps(s, ensure_ascii=False)
181
-
182
-
183
- def find_drifted_commands():
184
- """找出所有 command↔skill 配对且 description 漂移的命令名。
185
- (复用 check_carriers 的相似度判断)
186
- """
187
- from difflib import SequenceMatcher
188
- drifted = []
189
- if not SKILLS_DIR.is_dir():
190
- return drifted
191
- for d in sorted(SKILLS_DIR.iterdir()):
192
- if not d.is_dir() or not (d / 'SKILL.md').is_file():
193
- continue
194
- name = d.name
195
- cmd = _find_command_file(name)
196
- if not cmd:
197
- continue
198
- skill_fm, _, _ = _read_frontmatter(d / 'SKILL.md')
199
- cmd_fm, _, _ = _read_frontmatter(cmd)
200
- sd, cd = skill_fm.get('description', ''), cmd_fm.get('description', '')
201
- if not sd or not cd:
202
- continue
203
- sim = SequenceMatcher(None, sd.lower(), cd.lower()).ratio()
204
- if sim < 0.85:
205
- drifted.append(name)
206
- return drifted
207
-
208
-
209
- def main():
210
- ap = argparse.ArgumentParser(description='四载体 description 自动同步 (skill -> command)')
211
- ap.add_argument('command', nargs='?', default=None,
212
- help='只同步指定命令 (如 wl-search); 不传则同步所有漂移的')
213
- ap.add_argument('--dry-run', action='store_true', help='只预览不写文件')
214
- args = ap.parse_args()
215
-
216
- print('=' * 56)
217
- print('四载体 description 同步 (skill -> command)')
218
- print('=' * 56)
219
-
220
- if args.command:
221
- names = [args.command]
222
- else:
223
- names = find_drifted_commands()
224
- if not names:
225
- print('✅ 无漂移, 所有 command/skill description 已一致。')
226
- return 0
227
- print('发现 %d 个漂移命令: %s' % (len(names), ', '.join(names)))
228
- print()
229
-
230
- synced, skipped, errors = [], [], []
231
- for name in names:
232
- status, detail = sync_one(name, dry_run=args.dry_run)
233
- if status == 'synced':
234
- synced.append(name)
235
- print(' [✓] %s' % name)
236
- print(' %s' % detail)
237
- elif status == 'would-sync':
238
- synced.append(name)
239
- print(' [~] %s (dry-run)' % name)
240
- print('%s' % detail)
241
- elif status == 'skipped':
242
- skipped.append(name)
243
- else:
244
- errors.append((name, detail))
245
- print(' [✗] %s: %s' % (name, detail))
246
-
247
- print()
248
- verb = '将同步' if args.dry_run else '已同步'
249
- print('%s: %d 同步 / %d 跳过 / %d 错误'
250
- % (verb, len(synced), len(skipped), len(errors)))
251
- if not args.dry_run and synced:
252
- print('提示: 重跑 check_carriers.py 验证一致性。')
253
- if errors:
254
- return 1
255
- return 0
256
-
257
-
258
- if __name__ == '__main__':
259
- sys.exit(main())