@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
@@ -0,0 +1,261 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ report_snapshot.py — /wl-report 的一次性数据聚合器 (性能优化)
4
+
5
+ 问题: wl-report skill 让 AI 逐个 cat/ls/git log 收集数据, 实测产生 5-7 次工具调用、
6
+ 多轮思考。本脚本把 report 需要的全部数据源**一次返回**, 把 7 步顺序采集压成 2 步。
7
+
8
+ 与 status_snapshot.py 同构 (Fast Path 模式), 只是数据源换成 report 需要的:
9
+ 1. .developer — 当前开发者 (报告归属 + 全局过滤键)
10
+ 2. task.py list --mine — 我创建/被指派的任务
11
+ 3. journal/feedback — 今天的学习/反馈事件
12
+ 4. git log --author — 我的提交 (扫 data/code/ 各项目)
13
+ 5. drafts/ + specs/prd — 我写的 PRD/草稿
14
+
15
+ 输出: JSON, 缺失的字段标 null 并注明原因。AI 拿到后直接写报告, 不用再探查。
16
+ 所有数据严格按 .developer 里的开发者名过滤 (report 的隔离铁律)。
17
+
18
+ 用法:
19
+ python report_snapshot.py # 当前仓库, 默认今天
20
+ python report_snapshot.py /path/repo # 指定仓库
21
+ python report_snapshot.py --days 7 # 近 7 天 (周报)
22
+ """
23
+ import json
24
+ import os
25
+ import subprocess
26
+ import sys
27
+ from datetime import datetime, timedelta
28
+
29
+
30
+ def _repo_root():
31
+ """优先用命令行参数, 否则从环境反推 (.qoder 标记)。"""
32
+ args = [a for a in sys.argv[1:] if not a.startswith("--")]
33
+ if args and os.path.isdir(args[0]):
34
+ return os.path.abspath(args[0])
35
+ d = os.getcwd()
36
+ for _ in range(8):
37
+ if os.path.isdir(os.path.join(d, ".qoder")):
38
+ return d
39
+ d = os.path.dirname(d)
40
+ return os.getcwd()
41
+
42
+
43
+ def _days():
44
+ """--days N 参数, 默认 1 (今天/日报)。"""
45
+ for i, a in enumerate(sys.argv):
46
+ if a == "--days" and i + 1 < len(sys.argv):
47
+ try:
48
+ return int(sys.argv[i + 1])
49
+ except ValueError:
50
+ pass
51
+ return 1
52
+
53
+
54
+ def _read_text(path, limit=4000):
55
+ """安全读文本文件, 不存在返回 None。"""
56
+ if not os.path.exists(path):
57
+ return None
58
+ try:
59
+ with open(path, "r", encoding="utf-8") as f:
60
+ return f.read(limit)
61
+ except Exception as e:
62
+ return f"<read_error: {e}>"
63
+
64
+
65
+ def _parse_developer(text):
66
+ """从 .developer 文本里取 name= 行的值; 无则取首行。"""
67
+ if not text:
68
+ return None
69
+ for line in text.splitlines():
70
+ line = line.strip()
71
+ if line.startswith("name="):
72
+ return line[5:].strip()
73
+ first = text.splitlines()[0].strip() if text.strip() else None
74
+ return first or None
75
+
76
+
77
+ def _run_script(script_rel, repo, args=None, timeout=20):
78
+ """运行 .qoder/scripts 下的脚本, 返回 (stdout, ok)。"""
79
+ script = os.path.join(repo, script_rel)
80
+ if not os.path.exists(script):
81
+ return None, False
82
+ try:
83
+ cmd = [sys.executable, script] + (args or [])
84
+ r = subprocess.run(
85
+ cmd, cwd=repo, capture_output=True, text=True, timeout=timeout,
86
+ encoding="utf-8", errors="replace",
87
+ )
88
+ return (r.stdout.strip() if r.returncode == 0 else r.stderr.strip()), r.returncode == 0
89
+ except Exception as e:
90
+ return f"<run_error: {e}>", False
91
+
92
+
93
+ def _git_log_for_author(repo, author, days):
94
+ """扫 data/code/ 下各项目的 git log, 按作者过滤。返回 {project: [commits]}。"""
95
+ code_root = os.path.join(repo, "data", "code")
96
+ if not os.path.isdir(code_root):
97
+ return None
98
+ since = (datetime.now() - timedelta(days=days)).strftime("%Y-%m-%d")
99
+ result = {}
100
+ try:
101
+ for name in sorted(os.listdir(code_root)):
102
+ proj = os.path.join(code_root, name)
103
+ if not os.path.isdir(os.path.join(proj, ".git")):
104
+ continue
105
+ try:
106
+ r = subprocess.run(
107
+ ["git", "-C", proj, "log", f"--author={author}",
108
+ f"--since={since}", "--oneline", "-n", "50"],
109
+ capture_output=True, text=True, timeout=8,
110
+ encoding="utf-8", errors="replace",
111
+ )
112
+ out = r.stdout.strip() if r.returncode == 0 else ""
113
+ if out:
114
+ commits = [l for l in out.splitlines() if l.strip()]
115
+ result[name] = commits[:50]
116
+ except Exception:
117
+ continue
118
+ except Exception:
119
+ pass
120
+ return result if result else {}
121
+
122
+
123
+ def _list_prds_by_author(repo, author, days):
124
+ """收集 workspace/specs/prd/*.md 和 members/*/drafts 里 author==developer 的 PRD。
125
+ frontmatter 里有 author 字段就按它过滤; 草稿目录按开发者归属过滤。"""
126
+ since = datetime.now() - timedelta(days=days + 30) # PRD 按文件宽松一点
127
+ hits = []
128
+
129
+ # 1. 团队归档的 PRD (按 frontmatter author 过滤)
130
+ prd_dir = os.path.join(repo, "workspace", "specs", "prd")
131
+ if os.path.isdir(prd_dir):
132
+ for name in sorted(os.listdir(prd_dir)):
133
+ if not name.endswith(".md"):
134
+ continue
135
+ fp = os.path.join(prd_dir, name)
136
+ try:
137
+ mtime = datetime.fromtimestamp(os.path.getmtime(fp))
138
+ if mtime < since:
139
+ continue
140
+ head = _read_text(fp, limit=600) or ""
141
+ # frontmatter 里找 author:
142
+ is_mine = False
143
+ if "author:" in head.lower() or "author =" in head.lower():
144
+ is_mine = author.lower() in head.lower()
145
+ if is_mine:
146
+ title = name[:-3]
147
+ hits.append({"file": f"workspace/specs/prd/{name}",
148
+ "title": title, "mtime": mtime.strftime("%Y-%m-%d")})
149
+ except Exception:
150
+ continue
151
+
152
+ # 2. 个人草稿 (按目录归属天然隔离, 整个目录都是该开发者的)
153
+ dev_drafts = os.path.join(repo, "workspace", "members", author, "drafts")
154
+ if os.path.isdir(dev_drafts):
155
+ for root, _dirs, files in os.walk(dev_drafts):
156
+ for fn in files:
157
+ if fn.endswith(".md"):
158
+ fp = os.path.join(root, fn)
159
+ try:
160
+ mtime = datetime.fromtimestamp(os.path.getmtime(fp))
161
+ if mtime >= since:
162
+ rel = os.path.relpath(fp, repo).replace("\\", "/")
163
+ hits.append({"file": rel, "title": fn[:-3],
164
+ "mtime": mtime.strftime("%Y-%m-%d")})
165
+ except Exception:
166
+ continue
167
+ return hits[:50] if hits else []
168
+
169
+
170
+ def _journal_today(repo, author, days):
171
+ """读该开发者 journal 目录近 days 天的 feedback/learning 事件。"""
172
+ journal = os.path.join(repo, "workspace", "members", author, "journal")
173
+ if not os.path.isdir(journal):
174
+ return None
175
+ since = datetime.now() - timedelta(days=days)
176
+ events = []
177
+ for fn in sorted(os.listdir(journal)):
178
+ fp = os.path.join(journal, fn)
179
+ if not os.path.isfile(fp):
180
+ continue
181
+ try:
182
+ mtime = datetime.fromtimestamp(os.path.getmtime(fp))
183
+ if mtime < since:
184
+ continue
185
+ if fn.endswith(".jsonl"):
186
+ # feedback.jsonl: 读近几天的事件行
187
+ with open(fp, encoding="utf-8") as f:
188
+ for line in f:
189
+ line = line.strip()
190
+ if not line:
191
+ continue
192
+ try:
193
+ ev = json.loads(line)
194
+ # 事件带时间戳则按时间过滤, 否则按文件 mtime 已过滤
195
+ events.append(ev)
196
+ except json.JSONDecodeError:
197
+ continue
198
+ elif fn.endswith((".yaml", ".yml")):
199
+ txt = _read_text(fp, limit=2000)
200
+ if txt:
201
+ events.append({"_file": fn, "_summary": txt[:500]})
202
+ except Exception:
203
+ continue
204
+ return events[:100] if events else []
205
+
206
+
207
+ def snapshot(repo=None, days=None):
208
+ """收集 wl-report 需要的全部数据, 返回 dict。"""
209
+ repo = repo or _repo_root()
210
+ days = days or _days()
211
+ qd = os.path.join(repo, ".qoder")
212
+
213
+ data = {"_repo": repo, "_ts": datetime.now().isoformat(timespec="seconds"),
214
+ "_days": days}
215
+
216
+ # 1. 开发者身份 (report 的全局过滤键)
217
+ dev_text = _read_text(os.path.join(qd, ".developer"))
218
+ developer = _parse_developer(dev_text)
219
+ data["developer"] = developer or "(未设置)"
220
+ data["developer_raw"] = dev_text
221
+
222
+ if not developer or developer == "(未设置)":
223
+ data["_note"] = "未设置 .developer, 无法按开发者过滤。先 /wl-init。"
224
+ return data
225
+
226
+ # 2-5 四个独立数据源并行采集 (task list 最慢~440ms, 并行后总耗时≈最慢的那个)
227
+ # task list / git log / journal / prds 互不依赖 → 用线程池同时跑, 砍掉串行等待。
228
+ from concurrent.futures import ThreadPoolExecutor
229
+ with ThreadPoolExecutor(max_workers=4) as pool:
230
+ f_tasks = pool.submit(_run_script, ".qoder/scripts/domain/task/task.py", repo,
231
+ ["list", "--mine"])
232
+ f_journal = pool.submit(_journal_today, repo, developer, days)
233
+ f_commits = pool.submit(_git_log_for_author, repo, developer, days)
234
+ f_prds = pool.submit(_list_prds_by_author, repo, developer, days)
235
+
236
+ task_out, task_ok = f_tasks.result()
237
+ data["my_tasks"] = task_out if task_ok else None
238
+ data["my_tasks_error"] = None if task_ok else task_out
239
+ data["journal"] = f_journal.result()
240
+ data["commits"] = f_commits.result()
241
+ data["my_prds"] = f_prds.result()
242
+
243
+ # 6. 报告日期范围 (供 AI 套模板)
244
+ today = datetime.now().strftime("%Y-%m-%d")
245
+ start = (datetime.now() - timedelta(days=days - 1)).strftime("%Y-%m-%d")
246
+ data["date_range"] = f"{start} ~ {today}" if days > 1 else today
247
+
248
+ return data
249
+
250
+
251
+ def main():
252
+ repo = None
253
+ args = [a for a in sys.argv[1:] if not a.startswith("--")]
254
+ if args:
255
+ repo = args[0]
256
+ data = snapshot(repo)
257
+ print(json.dumps(data, ensure_ascii=False, indent=2, default=str))
258
+
259
+
260
+ if __name__ == "__main__":
261
+ main()
@@ -0,0 +1,39 @@
1
+ # v3.0 路径自举: 引导到 common/bootstrap, 统一 sys.path 逻辑
2
+ import os as _o, sys as _s
3
+ _f = _o.path.abspath(__file__)
4
+ for _ in range(10):
5
+ _f = _o.path.dirname(_f)
6
+ _cp = _o.path.join(_f, 'foundation')
7
+ if _o.path.isfile(_o.path.join(_cp, 'bootstrap.py')):
8
+ break
9
+ if _cp not in _s.path: _s.path.insert(0, _cp)
10
+ from bootstrap import setup; setup()
11
+ from foundation.core.paths import get_repo_root
12
+
13
+ # role.py - 角色解析 (v3.0: 已并入 common/identity.py, 本文件为兼容转发层)
14
+ #
15
+ # 角色能力查询 (get_role/can_produce/can_consume/...) 已统一到 common/identity.py,
16
+ # 由 guard.py 强制执行角色边界。本文件保留是为了:
17
+ # 1. CLI 兼容: skill 脚本里 `ROLE=$(python role.py)` 仍能输出角色名
18
+ # 2. import 兼容: `from role import get_role` 仍可用 (转发到 identity)
19
+ #
20
+ # ⚠️ 新代码请直接用: from foundation.identity.identity import get_role / from foundation.identity.guard import ...
21
+ # 本文件未来随 M2 引擎重构可能移除。
22
+ import os
23
+ import sys
24
+
25
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
26
+ from foundation.identity.identity import get_role, get_permissions, get_capabilities, can_produce, can_consume # noqa: E402,F401
27
+
28
+ BASE = get_repo_root()
29
+
30
+
31
+ if __name__ == '__main__':
32
+ # CLI: 输出当前角色名 (供 skill 脚本动态读角色用)
33
+ # 用法: ROLE=$(python role.py)
34
+ try:
35
+ sys.stdout.reconfigure(encoding='utf-8')
36
+ except (AttributeError, IOError):
37
+ pass
38
+ r = get_role()
39
+ print(r or 'pm') # 无角色默认 pm (软引导, 不报错)