@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
@@ -21,24 +21,82 @@ User input: $ARGUMENTS
21
21
  ```bash
22
22
  R=$(python ~/.qoderwork/repo_root.py 2>/dev/null) || R=.
23
23
  ```
24
- > 后续脚本统一用 `python "$R/.qoder/scripts/xxx.py"`(task.py / team_sync.py 等)。
24
+ > 后续脚本统一用 `python "$R/.qoder/scripts/orchestration/wlkj.py" <命令>`。
25
+
26
+ ---
27
+
28
+ ## 🔌 禅道模式判定(先于路由,决定数据走向)
29
+
30
+ **禅道为唯一事实源**(已确认架构)。`/wl-task` 的 create/start/finish/reassign 优先走禅道 MCP;
31
+ 本地 task.json 降级为离线缓存 + 增强字段层(RICE/依赖甘特这些禅道没有的本地能力全部保留,叠加在禅道任务上)。
32
+
33
+ ### ⚠️ 归属安全(防改错队友任务,必须遵守)
34
+
35
+ 禅道是**每人一个账号**(凭据在 `workspace/members/{我}/.private/secrets/zentao.env`,gitignore),归属靠 member.json 的 `zentao_account` 映射 + 工具默认行为兜底:
36
+
37
+ - **看任务**:`my_workbench` / `list_my_tasks` / `list_execution_tasks` **默认只返回指派给我的**(assignedTo=我),不会看到队友。要看全队排期才传 `all=true`。
38
+ - **空账号直接拒绝**:member.json 没配 `zentao_account` 时,"看我的"工具返回 `🚫 未识别禅道身份`(不再静默降级返回全队)。原样转述,引导 `/wl-init`。
39
+ - **改任务前必校验**:start/finish/assign/update 拿到 task_id 后,**先 `get_task(id)` 看 assignedTo**:
40
+ - `assignedTo == 我` → 正常执行;
41
+ - `assignedTo == 别人` → **停下来问用户**"这是指派给 X 的任务,确认要改吗?",**不静默改**。
42
+ - **建任务**:`create_task` 的 `assignedTo` **默认指派给当前开发者**(从 member.json 的 zentao_account),显式传参才派别人。
43
+ - 隔离红线见 `contracts/isolation.md`「禅道同步」。
44
+
45
+ ### 第 0 步:探测禅道可达性
46
+ 调用禅道健康检查(QoderWork: `qw_mcp_call("check_zentao_status", {})` / CLI: `cap.mcp.call("check_zentao_status")`)——内网 + 凭据一道探测:
47
+ - **✅ 可达** → 走**禅道模式**(下方各动作的「禅道走法」)
48
+ - **❌ 不可达 / 未配置** → 走**本地模式**(下方各动作的原有 `task.py` 走法,离线兜底,不报错不阻塞)
49
+ - **用户明确说"离线"/"先存本地"** → 即使禅道可达也走本地模式
50
+
51
+ > 这套判定让禅道有就同步、没内网就静默回退 —— 符合 AGENTS.md「本地数据是唯一事实源、连接器是可选增强、断网不阻塞」原则。
52
+ > 探测本身要 1 次调用。**高频场景(看我的活)可省略探测**:直接 my_workbench,它内部自带内网/凭据门,不通会返回友好提示,不浪费一次往返。
53
+
54
+ ### 列表勾选式交互(禅道模式的统一范式)
55
+
56
+ 禅道所有关联/选择类操作都用这个范式 —— **PM/组长回个数字就完事,零学习成本**:
57
+
58
+ ```
59
+ 用户说一句话 → AI 调 list_* 工具搜候选 → 列 5-8 个带编号(附状态/数量)
60
+ → 用户回编号(支持 1,2 多选) → AI 调 create/link/resolve 执行
61
+ → 反馈 ✅ + 关联结果 → (可选)追问下一步
62
+ ```
63
+
64
+ **剧本示例(组长拆任务,跨执行多选):**
65
+ ```
66
+ 组长: 把 R-231 报价单导出拆成任务
67
+ AI: 匹配到需求 #761 报价单导出 ✅
68
+ 要拆到哪个执行?(最近 5 个):
69
+ [1] 执行#73 车险v2后端 进行中 · 14任务
70
+ [2] 执行#75 小程序改版 进行中 · 6任务
71
+ [3] 执行#71 理赔优化 已完成
72
+ 组长> 1,2 ← 支持多选 = 跨执行建任务
73
+ AI: 建议: ①后端导出接口(6h) ②前端按钮(3h) ③测试用例(2h)
74
+ 拆哪几个? (回 1,3 或 "全部")
75
+ 组长> 全部
76
+ AI: ✅ 6 个任务已建(2执行×3任务), 均挂 story=#761
77
+ ```
25
78
 
26
79
  ---
27
80
 
28
81
  ## ⚠️ STEP 0: 动作路由(看 $ARGUMENTS 第一个词)
29
82
 
30
83
  **判断 `$ARGUMENTS` 第一个词,立刻决定走哪条线。没有动作词 → 语义自动归档。**
84
+ > ⚠️ 边界:本命令管的是**本地任务工序**(建/查/排/归档)。禅道的**自然查询**("我的禅道任务""看下#301""Bug状态")
85
+ > 不走本命令 —— AI 直接用禅道 MCP 工具自然回答(见 AGENTS.md「禅道自然查询」),别把随口问拉进工作流。
86
+ > 禅道的**显式工序**(PRD 发禅道建需求、记工时、改任务状态)走下表 zentao 动作。
31
87
 
32
88
  | 用户说 / 第一个词 | 动作 | 跑什么 |
33
89
  |------------------|------|--------|
34
90
  | `create` / "建任务" / "加个任务" | **create** | `task.py create` |
35
- | `list` / "看任务" / "我的任务" / "有哪些任务" | **list** | `task.py list` |
91
+ | `list` / "看任务" / "有哪些任务" | **list** | `task.py list` |
36
92
  | `show` / "看下 XXX" / "XXX 怎么样了" | **show** | `task.py show <name>` |
37
93
  | `start` / "开始做" / "启动 XXX" | **start** | `task.py start <name>` |
38
94
  | `finish` / "做完了" / "完成了" | **finish** | `task.py finish` |
39
95
  | `archive` / "归档" | **archive** | `task.py archive <name>` |
40
96
  | `rank` / "排优先级" / "排一下" | **rank** | RICE 评估(见下) |
41
97
  | `plan` / "排期" / "甘特图" / "排个日期" | **plan** | `task.py gantt` + set-due |
98
+ | `sync` / "同步到禅道" | **sync** | 本地缓存 → 禅道(见 STEP 2) |
99
+ | `zentao` / "PRD发禅道" / "记工时" / "改任务状态" | **zentao** | 显式禅道工序(见 STEP 2 工序剧本) |
42
100
  | **(无动作词)** | **语义自动归档** | 见下 |
43
101
 
44
102
  ### 语义自动归档(无动作词时,AI 按这句话的意图判断)
@@ -46,7 +104,9 @@ R=$(python ~/.qoderwork/repo_root.py 2>/dev/null) || R=.
46
104
  - "PRD 写完了,建个任务吧" / "把刚才那个需求建成任务" → **create**(带 PRD 衔接)
47
105
  - "现在要做哪些?" / "接下来干啥" → **list --ready**(只看能立刻开始的)
48
106
  - "卡住的有哪些?" → **list --blocked**
107
+ - "PRD 发禅道" / "记下今天 3 小时工时" → **zentao**(显式工序)
49
108
  - 拿不准意图 → **先 list 给全貌,再问用户要操作哪个**
109
+ - ⚠️ "我的任务" / "看下我的禅道" / "Bug 状态" 这类**纯查询**——不归档进工作流,AI 直接自然回答
50
110
 
51
111
  ---
52
112
 
@@ -54,17 +114,19 @@ R=$(python ~/.qoderwork/repo_root.py 2>/dev/null) || R=.
54
114
 
55
115
  这三道是"鲁棒"的保障,防止脚本崩在半路、空数据吓到用户。
56
116
 
57
- ### 检查 ①|身份在不在
117
+ > ①②相互独立 → **同一条消息并发探测**(一次回合发两个,别逐个等);③是执行 task.py 后才看退出码,归 STEP 2。
118
+
119
+ ### 检查 ①|身份在不在(与②并发)
58
120
  ```bash
59
- python -c "import sys; sys.path.insert(0,r'$R/.qoder/scripts'); from common.paths import get_developer, get_repo_root; d=get_developer(get_repo_root()); print(d or 'MISSING')"
121
+ python -c "import sys; sys.path.insert(0,r'$R/.qoder/scripts'); from foundation.core.paths import get_developer, get_repo_root; d=get_developer(get_repo_root()); print(d or 'MISSING')"
60
122
  ```
61
123
  - 有名字 → 继续。
62
124
  - `MISSING` → **停下,提示**:"还没初始化身份,先跑 `/wl-init 你的名字 产品`"。不要继续往下跑(create 会把 assignee 写成空)。
63
125
 
64
- ### 检查 ②|tasks 目录在不在
126
+ ### 检查 ②|tasks 目录在不在(与①并发)
65
127
  - 不存在 / 空 → create 直接建;list/show/rank 等读操作 → **友好提示** "还没有任务,要建一个吗?" 而不是报错。
66
128
 
67
- ### 检查 ③|脚本退出码
129
+ ### 检查 ③|脚本退出码(STEP 2 执行 task.py 后看)
68
130
  - `task.py` 任何子命令返回**非 0**,**先读 stderr 原样告诉用户**,不吞错。
69
131
  - 常见非 0:`4` = 权限不足(零信任 ACL,只有 creator/assignee/admin 能改任务);`1` = 任务不存在 / task.json 缺失。
70
132
  - 权限不足时提示:"这个任务不是你建的,找 `{assignee}` 或管理员操作。" 不要自己绕过 ACL。
@@ -80,34 +142,61 @@ python -c "import sys; sys.path.insert(0,r'$R/.qoder/scripts'); from common.path
80
142
  如果刚跑完 `/wl-prd-full`(或用户说"把刚才那个需求建成任务"),先确认 PRD 在哪:
81
143
  ```bash
82
144
  # 找最近的 PRD 草稿(当前开发者的)
83
- ls workspace/members/<developer>/drafts/REQ-*.md 2>nul
145
+ ls workspace/members/<developer>/drafts/REQ-*.md 2>/dev/null
84
146
  # 或已发布的
85
- ls data/docs/prd/REQ-*.md 2>nul
147
+ ls data/docs/prd/REQ-*.md 2>/dev/null
86
148
  ```
87
149
  - 有 PRD → 从 PRD 提取**标题、模块、验收点**填进任务,**别让用户重述一遍**。
88
150
  PRD 的 REQ-ID 记到 task.json 的 `tags` 里(如 `["REQ-2026-042"]`),方便双向追溯。
89
151
  - 无 PRD → 用 $ARGUMENTS 里用户给的一句话当标题。
90
152
 
91
153
  **B. 建任务**
154
+
155
+ **禅道走法(禅道可达时优先):** 需求建在禅道、任务也拆在禅道,本地只缓存一份。
92
156
  ```bash
93
- python "$R/.qoder/scripts/task.py" create "<标题>" --priority <P0|P1|P2|P3>
157
+ # 1. 先确认需求在禅道(从 PRD 发布时已建, 或现在建):
158
+ # create_story(product_id=<PID>, title=<PRD标题>, spec=<描述>)
159
+ # ★ 建完需求后, 主动问 PM "要关联到哪个版本?(本周/未来)", 用
160
+ # list_builds(project_id=<项目ID>) 列候选 → link_story_to_build 关联
161
+ # (详见下方 story 域「建需求后主动问版本」剧本)
162
+ # 2. 组长拆任务(列表勾选式):
163
+ # 先 list_executions() 列候选执行让用户选 → create_task(execution_id=<EID>,
164
+ # name=<任务名>, assignedTo=<账号>, type=devel/test/design, estimate=<h>, story=<需求ID>)
165
+ # 3. 同步一份到本地缓存(增强字段层用):
166
+ # python "$R/.qoder/scripts/domain/task/task.py" create "<标题>" --priority <P0|P1|P2|P3>
167
+ ```
168
+
169
+ **本地走法(禅道不可达 / 用户要离线时):**
170
+ ```bash
171
+ python "$R/.qoder/scripts/domain/task/task.py" create "<标题>" --priority <P0|P1|P2|P3>
94
172
  ```
95
173
  - `--assignee` 不填则默认当前开发者(产品建的常指派给开发,可加 `--assignee 老李`)。
96
174
  - `--slug` 不填则自动从标题生成(中文标题也能生成,但建议给个英文 slug 如 `--slug login-export`)。
97
175
  - 同名撞库时脚本会自动加后缀(creator 短标记),**不阻塞**,告诉用户最终名字即可。
176
+ - **禅道恢复后**:用 `sync` 动作把本地缓存推回禅道(见 STEP 2 sync)。
98
177
 
99
178
  **C. 建完必做**
100
179
  1. 告诉用户任务路径:`workspace/tasks/{MM-DD-slug}/`,里面有 `prd.md` 模板(若已有 PRD,可把 PRD 内容贴进去或引用)。
101
180
  2. **待排期池 >3 个任务时**,主动建议:"要不要我跑个 rank 排下优先级?"
102
181
  3. 自动同步(用户永不碰 git):
103
182
  ```bash
104
- python "$R/.qoder/scripts/team_sync.py" push
183
+ python "$R/.qoder/scripts/domain/task/team_sync.py" push
105
184
  ```
106
185
 
107
186
  ### list —— 看任务(一次取全,别串行)
108
187
 
188
+ **禅道走法(QoderWork 第一宿主,丝滑首选):**
189
+ ```
190
+ qw_mcp_call("my_workbench", {}) ← ★一行看全(任务+需求+Bug), 跨执行/产品聚合
191
+ qw_mcp_call("list_my_tasks", {"status":"wait"}) ← 只看我未开始的任务
192
+ qw_mcp_call("list_my_tasks", {"status":"doing"}) ← 只看我进行中的
193
+ ```
194
+ > QoderWork 用 `qw_mcp_call(toolName, arguments)` 直接调(工具已起进程,省去 list/get 两步元调用)。
195
+ > **禁逐个 list_execution_tasks**(会漏早期执行里的任务,且每次都要 execution_id)。"我的全部"用 my_workbench/list_my_tasks。
196
+
197
+ **本地走法(离线缓存 / 禅道不可达):**
109
198
  ```bash
110
- python "$R/.qoder/scripts/task.py" list [--mine] [--status <s>] [--ready] [--blocked]
199
+ python "$R/.qoder/scripts/domain/task/task.py" list [--mine] [--status <s>] [--ready] [--blocked]
111
200
  ```
112
201
 
113
202
  | flag | 用途 |
@@ -126,44 +215,256 @@ python "$R/.qoder/scripts/task.py" list [--mine] [--status <s>] [--ready] [--blo
126
215
  - `--ready` 空 → "当前没有能立刻开始的任务,都在等依赖。要看卡住的吗?(--blocked)"
127
216
 
128
217
  ### show —— 看单个任务详情
129
- ```bash
130
- python "$R/.qoder/scripts/task.py" show <任务名或目录>
131
- ```
218
+
219
+ **禅道走法(QoderWork):** `qw_mcp_call("get_task", {"task_id": <禅道ID>})` ← 含工时预计/消耗/剩余/关联需求
220
+ **本地走法:** `python "$R/.qoder/scripts/domain/task/task.py" show <任务名或目录>`
221
+
132
222
  任务名支持模糊匹配(脚本内部 `resolve_task_dir` 会解析)。读完把关键字段(状态/优先级/assignee/due/依赖/prd路径)整理给用户。
133
223
 
134
224
  ### start —— 启动任务(移交开发工位的信号)
135
- ```bash
136
- python "$R/.qoder/scripts/task.py" start <任务名>
137
- ```
225
+
226
+ **禅道走法(QoderWork,优先):** `qw_mcp_call("start_task", {"task_id": <禅道ID>, "left": <剩余工时h>})`
227
+ **本地走法:** `python "$R/.qoder/scripts/domain/task/task.py" start <任务名>`
138
228
  - 状态 `planning → in_progress`,成为活跃任务(`.current-task`)。
139
229
  - 这是产品→开发的交接点:start 后开发就能 `/wl-code` 了。
140
230
  - **ACL**:只有 creator/assignee/admin 能 start。被拒(返回 4)→ 提示找对应人。
231
+ - **禅道+本地双写**:先 qw_mcp_call start_task 同步禅道,再 task.py start 同步本地缓存(双源一致)。
141
232
 
142
233
  ### reassign —— 改派任务负责人(多角色协作必需)
143
- ```bash
144
- python "$R/.qoder/scripts/task.py" reassign <任务名> <新负责人>
145
- ```
234
+
235
+ **禅道走法(QoderWork):** `qw_mcp_call("assign_task", {"task_id": <禅道ID>, "account": <禅道账号>})`
236
+ - 账号先用 `qw_mcp_call("list_users", {})` 列候选让用户选(**列表勾选式**)。
237
+ **本地走法:** `python "$R/.qoder/scripts/domain/task/task.py" reassign <任务名> <新负责人>`
146
238
  - 场景:PM 建任务(assignee 默认是自己)后,要转给开发老李做。
147
239
  - 改派后新负责人才能 `start`/`finish`(零信任 ACL 按 assignee 判定)。
148
240
  - **ACL**:只有 **creator/admin** 能改派(assignee 自己不能转手甩锅)。被拒(返回 4)→ 提示找 creator。
149
- - **改派历史自动记录**到 task.json 的 `assignee_history`(谁在何时从谁转给谁),便于追溯。
150
241
 
151
242
  ### finish —— 完成当前任务
152
- ```bash
153
- python "$R/.qoder/scripts/team_sync.py" push
154
- ```
243
+
244
+ **禅道走法(QoderWork,优先):** `qw_mcp_call("finish_task", {"task_id": <禅道ID>, "consumed": <本次消耗h>, "left": 0})`
245
+ - 禅道要求填消耗工时,**先问用户"这次花了多少小时?"** 再调。
246
+ **本地走法:** `python "$R/.qoder/scripts/domain/task/task.py" finish` → 然后 `team_sync.py push`
155
247
  - 活跃任务状态 → `completed`,活跃指针清除。
156
248
  - 完成后提示:要不要 `archive` 归档?要不要看下个 `--ready` 的任务?
157
- - 自动同步 push。
158
249
 
159
- ### archive —— 归档(移到 .qoder/archive/{YYYY-MM}/)
160
- ```bash
161
- python "$R/.qoder/scripts/task.py" archive <任务名>
162
- ```
250
+ ### archive —— 归档(移到 .qoder/archive/{YYYY-MM}/})
251
+
252
+ **本地走法:** `python "$R/.qoder/scripts/domain/task/task.py" archive <任务名>`
163
253
  - 事务化:先 move 成功再改 status,move 失败原任务完好。
164
254
  - **自动清理孤儿依赖**:归档后,其它任务里对它的 `blocked_by/blocks/children/parent` 引用会被扫除(防止僵尸任务永远 blocked)。
165
255
  - 提示用户:归档后任务不再出现在 list 里,但归档目录可翻历史。
166
256
 
257
+ ### zentao —— 禅道全域(需求/任务/Bug/规划工时,一站式)
258
+
259
+ 禅道是完整的项目管理域(63 工具)。`/wl-task` 是唯一入口,按自然语言判断属于哪个域,直接调对应禅道 MCP 工具。
260
+
261
+ **先按这句话的意图判断属于哪个域:**
262
+
263
+ | 用户说的关键词 | 域 | 典型动作 |
264
+ |---------------|-----|---------|
265
+ | 需求/PRD/story/发布/计划/版本 | **需求(story)** | 建需求/看需求/改状态/关联 |
266
+ | 任务/task/拆任务/开始/完成/工时 | **任务(task)** | 建/看/开始/完成/记工时 |
267
+ | Bug/缺陷/问题/bug/解决/激活 | **Bug** | 提bug/看/确认/解决/关闭 |
268
+ | 产品/项目/执行/迭代/发布/计划/工时统计 | **规划+工时** | 建产品/执行/版本, 看工时 |
269
+
270
+ #### 域路由表(自然语言 → 工具调用,低 AI 调用:每句直达一个工具)
271
+
272
+ **① 需求(story)域**
273
+ ```
274
+ 用户说 → 工具
275
+ "PRD 发禅道"/"建需求" → create_story(product_id=?, title=<PRD标题>, spec=<描述>)
276
+ (不知道产品先 list_products() 让用户选)
277
+ "看下需求"/"有哪些需求" → list_stories(product_id=?)
278
+ "看需求#761详情" → get_story_detail(story_id=761)
279
+ "需求改状态"(draft→reviewing等) → change_story_status(story_id=?, status=?)
280
+ "需求关联到计划/版本/执行" → link_story_to_plan / link_story_to_build / link_story_to_execution
281
+ ```
282
+
283
+ #### 建需求后主动问版本(★ 你们的版本=项目级 build,PM 最高频关联动作)
284
+
285
+ > 你们的「版本」是**项目级 build**(项目#9 下按周 `V+日期`,如 `V20260625`),不是产品级 plan/release。
286
+ > 一个需求可能**同时进当前版本 + 未来版本**(一次发部分、下次发另一部分),所以**显式让 PM 多选**,不自动关联。
287
+
288
+ **剧本(建需求/评审通过后,AI 主动列 build 让 PM 勾选):**
289
+ ```
290
+ PM: 需求#761建好了
291
+ AI: 要关联到哪个版本?(最近 6 个 build):
292
+ [1] #22 V20260625 (本周, 已关联 9 个 bug)
293
+ [2] #21 V20260618 (上周)
294
+ [3] #16 V20260604
295
+ [4] #11 V20260429 (已含 25 个需求)
296
+ ...
297
+ PM> 1,3 ← 支持多选 = 一个需求进当前版本(本周发)+未来版本
298
+ AI: ✅ 需求#761 已关联 build#22(本周) + #16(未来)
299
+ ```
300
+ - 候选用 `list_builds(project_id=<项目ID>)`(项目维度,最直观)或 `list_builds(product_id=<产品ID>)` 取最近 6 个,按时间倒序编号。
301
+ - 关联调 `link_story_to_build(build_id=<选中>, stories=[<需求ID>])`,**逐个 build 调一次**(每个版本独立关联)。
302
+ - **PM 也可主动触发**:"把#761 关联到本周版本" → AI 查本周 build 再关联。
303
+ - 不知道项目/产品 ID 时先 `list_projects()` / `list_products()` 让用户勾选。
304
+
305
+ **② 任务(task)域**(详见前面 create/start/finish 段,这里补查询)
306
+ ```
307
+ "看我的任务"/"这周干啥"/"我手头有什么活" → ★my_workbench() 或 list_my_tasks()
308
+ ↑ 跨所有执行全量聚合, 不漏! 别用 list_execution_tasks 逐个查
309
+ "看我未开始的" / "看进行中的" → list_my_tasks(status='wait') / list_my_tasks(status='doing')
310
+ "看某执行的任务"(已知执行ID) → list_execution_tasks(execution_id=?) ← 默认只看我的
311
+ "看全队任务" → list_execution_tasks(execution_id=?, all=true)
312
+ "拆需求为任务"/"建任务" → create_task(execution_id=?, name=?, ...) ← 不填 assignedTo 默认指派给我
313
+ "看任务#301" → get_task(task_id=301) ← 含工时预计/消耗/剩余
314
+ "任务改指派/改工时/改截止" → update_task(task_id=?, ...) 或 assign_task(task_id=?, account=?)
315
+ "记工时"/"今天干了3小时" → log_effort(task_id=?, consumed=3, left=?)
316
+ "看任务工时记录" → list_effort(task_id=?)
317
+ ```
318
+
319
+ > **🚫 铁律: 问啥查啥, 禁止跨域冒充 (v3.1.5)**
320
+ > 用户问「任务」就查 task 域(my_workbench/list_my_tasks/get_task), 问「需求」查 story 域, 问「Bug」查 bug 域。
321
+ > **查不到必须如实说**「没有指派给你的任务」+ 贴查询证据, **绝不拿需求/Bug 顶替任务来回答**(用户实测踩坑:
322
+ > 问任务却收到需求列表)。归属过滤空账号时工具会拒绝并提示 /wl-init, 把拒绝原样转述, 不绕过。
323
+
324
+ **③ Bug 域**
325
+ ```
326
+ "提个Bug"/"发现个问题" → create_bug(product_id=?, title=?, openedBuild=?, severity=1-4)
327
+ "看我的Bug"/"我的缺陷" → ★list_my_bugs() ← 跨所有产品全量, 不漏! 别逐产品查
328
+ "看某产品的Bug"(已知产品ID) → list_bugs(product_id=?) ← 默认只看指派给我的
329
+ "看全队的Bug" → list_bugs(product_id=?, all=true)
330
+ "看Bug#101" → get_bug(bug_id=101)
331
+ "Bug解决了"/"修好了" → resolve_bug(bug_id=?, resolution='fixed', resolvedBuild=?)
332
+ "确认Bug"/"认领Bug" → confirm_bug(bug_id=?)
333
+ "Bug没改好,重开" → activate_bug(bug_id=?)
334
+ "关闭Bug" → close_bug(bug_id=?)
335
+ ```
336
+
337
+ **④ 规划+工时域**
338
+ ```
339
+ "有哪些产品/项目/执行" → list_products() / list_projects() / list_executions()
340
+ "建产品/项目/执行/版本/计划" → create_product / create_project / create_execution / create_build / create_plan
341
+ "看执行#73" → get_execution(execution_id=73)
342
+ "需求/Bug 关联计划/版本" → link_story_to_plan / link_bug_to_plan / link_*_to_build
343
+ ```
344
+
345
+ #### 通用规则(所有禅道操作)
346
+ - **不知道 ID 时**:先调 list_* 让用户勾选(列表勾选式,别让用户背 ID)。
347
+ - **写操作默认归属校验**:工具层已强制——改任务/Bug 前自动校验 assignedTo==我,不是我的会拒绝并提示归属人。
348
+ - **建任务/Bug 不填 assignedTo**:默认指派给当前开发者自己(防共享账号误派)。
349
+ - **禅道不可达**:提示"内网未连,看本地缓存用 `/wl-task list`",不报错。
350
+ - **🚫 跨域不冒充**:问任务查任务、问需求查需求。查不到如实说"没有"+贴证据,绝不拿别的域顶替。
351
+
352
+ #### 🔌 宿主路由:禅道怎么调(显式工序操作用)
353
+
354
+ > 仅适用于本文件的**显式工序操作**(建需求/建任务/改状态/记工时)。
355
+ > 自然查询("我的任务""看下 #301")不走本表 —— AI 直接用禅道 MCP 工具自然回答,不拉入 /wl-task。
356
+
357
+ | 宿主 | 调用入口 |
358
+ |------|---------|
359
+ | **QoderWork** | `qw_mcp_call("工具名", {参数})`(复用已起的 zentao 进程;工具名确定时直接 call,省 qw_mcp_list/get 两步) |
360
+ | **Qoder IDE / Quest** | `mcp__qoder-zentao__工具名(参数)` |
361
+ | **CLI / 其他** | `cap.mcp.call("工具名", {参数})` |
362
+
363
+ #### 🎯 工序操作剧本(显式建/改,非查询)
364
+
365
+ > 下面是 /wl-task 的**工序动作**(建需求/建任务/改状态/记工时),需要流程保障。
366
+ > 查询类(看我的活、看进度)见 AGENTS.md「禅道自然查询」,直接用 MCP 工具回答,不在这里。
367
+
368
+ **① 发需求到禅道(PM 产出动作 · 完整工序,别发完就停)**
369
+ ```
370
+ 触发: "PRD 发禅道"/"建需求"/"发布到禅道"/"把这个需求发上去"
371
+
372
+ ★这是 PM 最重要的产出动作, 必须走完整工序(7步), 发完一句"✅已创建"就停 = 失败。
373
+
374
+ AI 步骤:
375
+ 1. 读 PRD: 把本地 PRD.md 全文读出来 (背景/目标/功能详细/验收标准), 别只取标题
376
+ 2. 确认产品: 不知道就 qw_mcp_call("list_products", {}) 勾选 (你们活跃: #1 ICS2.0/#12 车辆作业/#13 车辆异常)
377
+ 3. ★建需求(spec 必填全文, 不是一句摘要):
378
+ qw_mcp_call("create_story", {
379
+ "product_id": <选>,
380
+ "title": <PRD标题>,
381
+ "spec": <★PRD全文: 背景+目标+功能详细说明+验收标准, 用 markdown>,
382
+ "pri": <1-4, 默认3>,
383
+ "category": "feature"
384
+ })
385
+ → 拿到 story_id
386
+
387
+ 4. ★提评审 (不跳过! draft 不评审等于没发):
388
+ 告诉用户: "需求 #X 已建(draft), 建议提交评审让相关人过目"
389
+ qw_mcp_call("change_story_status", {"story_id": <X>, "status": "reviewing"})
390
+ → 状态 draft→reviewing, 评审人会收到通知
391
+
392
+ 5. ★关联计划 (你们版本=项目级 plan, 不关联计划的需求等于孤儿):
393
+ qw_mcp_call("list_plans", {"product_id": <选>}) 取最近计划 → 列给用户:
394
+ [1] #15 2026年6月迭代 [2] #14 2026年7月迭代
395
+ 用户选 → qw_mcp_call("link_story_to_plan", {"plan_id": <选>, "stories": [<story_id>]})
396
+
397
+ 6. 关联版本 (build, 可选, 发布管理用):
398
+ qw_mcp_call("list_builds", {"project_id": 9}) 取最近 build → 问用户要不要关联
399
+ 要 → qw_mcp_call("link_story_to_build", {"build_id": <选>, "stories": [<story_id>]})
400
+
401
+ 7. ★原型图 (有则处理, 无则跳过):
402
+ 有本地原型文件 → qw_mcp_call("upload_file", {"file_path":"<原型路径>","object_type":"story","object_id":<X>})
403
+ - 上传成功: 告诉用户附件已挂
404
+ - 上传失败(禅道REST限制): 给降级方案(手动上传/截图嵌入), 并保留本地预览路径
405
+ 无原型: 跳过此步
406
+
407
+ 8. ★富反馈 (告诉用户做了什么, 不是一句"✅"):
408
+ 汇总输出:
409
+ ┌──────────────────────────────────┐
410
+ │ ✅ 需求已发布到禅道 │
411
+ │ 标题: <PRD标题> │
412
+ │ ID: #<X> 地址: <URL>/story-view-<X>.html │
413
+ │ 产品: <选的产品> │
414
+ │ 状态: reviewing(已提评审) │
415
+ │ 关联: 计划 #<P> 版本 #<B>(若有) │
416
+ │ 原型: 已上传/降级(见上) │
417
+ │ spec: 已填全文(<N>字) │
418
+ └──────────────────────────────────┘
419
+ → 追问: "评审过了要我关联到执行让研发拆任务吗? (list_executions 勾选)"
420
+ ```
421
+ ⚠️ 铁律: spec 必须是 PRD 全文(背景/目标/功能/验收), 不是一句话摘要。发完"✅已创建"就停、
422
+ 不提评审不关联计划 = 半成品发布, 用户感受不到价值(实测踩坑)。
423
+
424
+
425
+ **② 拆任务/改状态/记工时(组长·研发工序)**
426
+ ```
427
+ 触发: "拆任务"/"把需求拆成任务"/"开始做#301"/"完成了"/"今天干了3小时"
428
+ AI: 拆任务(列表勾选式, 跨执行多选):
429
+ 1. qw_mcp_call("list_executions", {}) 列候选执行 → 用户选 [1,2]
430
+ 2. AI 建议任务拆分模板(基于需求): "建议: ①后端接口(6h) ②前端(3h) ③测试(2h), 拆哪几个?"
431
+ 3. 用户回"全部" → qw_mcp_call("create_task", {"execution_id":<eid>,"name":<名>,"assignedTo":<我>,"type":"devel","estimate":6,"story":<sid>}) 逐个建, ✅ 汇总
432
+ 改状态(QoderWork 用 qw_mcp_call):
433
+ "开始做#301" → qw_mcp_call("start_task", {"task_id":301,"left":6}) → ✅ + 追问"要同步本地 task.py start 吗?"
434
+ "完成了" → 先问"花了多少小时?" → qw_mcp_call("finish_task", {"task_id":301,"consumed":X,"left":0})
435
+ "记工时" → qw_mcp_call("log_effort", {"task_id":301,"consumed":3,"left":<剩余>})
436
+ ```
437
+
438
+ ---
439
+
440
+ ### sync —— 本地缓存同步回禅道(离线恢复后用)
441
+
442
+ > v3.1.3 起有**独立脚本** `zentao_sync.py`,安全优先(不拉错/不覆盖队友)。
443
+ > **隔离红线**见 `contracts/isolation.md`「禅道同步」。
444
+
445
+ 离线时本地建的任务,禅道恢复后用脚本推上去。**默认 dry-run 预览,确认无误再加 `--apply`:**
446
+
447
+ ```bash
448
+ # 看同步状态(只读:本地哪些任务关联了禅道,哪些待推)
449
+ python "$R/.qoder/scripts/domain/task/zentao_sync.py" status
450
+
451
+ # 推送本地任务到禅道(先 dry-run 预览)
452
+ python "$R/.qoder/scripts/domain/task/zentao_sync.py" push --execution_id <执行ID> # 预览
453
+ python "$R/.qoder/scripts/domain/task/zentao_sync.py" push --execution_id <执行ID> --apply # 真推
454
+
455
+ # 拉取禅道任务到本地(仅 assignedTo=我,队友的拉不进来)
456
+ python "$R/.qoder/scripts/domain/task/zentao_sync.py" pull <执行ID> --apply
457
+ ```
458
+
459
+ **安全保证(脚本强制,不靠 AI 自觉):**
460
+ - **幂等**:本地已有 `zentao_id` 的任务跳过(不重复建)。
461
+ - **归属硬过滤**:拉取只取 `assignedTo == 我的禅道账号`;推送只推 `creator == 我` 的本地任务。
462
+ - **防误改队友**:推送时若禅道同名任务的 `assignedTo` ≠ 我 → **拒绝更新**。
463
+ - **归属映射**:本地 developer → 禅道 account 靠 `member.json` 的 `zentao_account` 字段,没配则拒绝同步(宁可不同步也不猜)。
464
+ - **禅道不可达**:提示"内网未连,看本地缓存用 `/wl-task list`",退出码 5 不报错。
465
+
466
+ > 提示:首次用前确认 `member.json` 配了 `zentao_account`(如 hupan56→hupan),见 `contracts/isolation.md`。
467
+
167
468
  ---
168
469
 
169
470
  ## STEP 3: RICE 排序(rank 动作 —— 产品的优先级决策)
@@ -172,7 +473,7 @@ python "$R/.qoder/scripts/task.py" archive <任务名>
172
473
 
173
474
  ### 一次取全(别逐个问)
174
475
  ```bash
175
- python "$R/.qoder/scripts/task.py" list --status planning
476
+ python "$R/.qoder/scripts/domain/task/task.py" list --status planning
176
477
  ```
177
478
  拿到所有待排任务,**一次性**评估,不要一个任务问四轮 R/I/C/E。
178
479
 
@@ -203,23 +504,23 @@ python "$R/.qoder/scripts/task.py" list --status planning
203
504
 
204
505
  ### 设置截止日期
205
506
  ```bash
206
- python "$R/.qoder/scripts/task.py" set-due <任务名> <YYYY-MM-DD>
507
+ python "$R/.qoder/scripts/orchestration/wlkj.py" task set-due <任务名> <YYYY-MM-DD>
207
508
  ```
208
509
 
209
510
  ### 依赖关系(谁挡谁)
210
511
  ```bash
211
512
  # A 被 B 阻塞(要等 B 完成)
212
- python "$R/.qoder/scripts/task.py" block <A任务名> <B任务名>
513
+ python "$R/.qoder/scripts/orchestration/wlkj.py" task block <A任务名> <B任务名>
213
514
  # 解除
214
- python "$R/.qoder/scripts/task.py" unblock <A任务名> <B任务名>
515
+ python "$R/.qoder/scripts/orchestration/wlkj.py" task unblock <A任务名> <B任务名>
215
516
  ```
216
517
  - **循环依赖自动检测**:脚本会 BFS 检查,发现环就拒绝(返回 1)。
217
518
  - 反向 `blocks` 关系自动维护(A 被 B 阻塞时,B 的 blocks 自动加 A)。
218
519
 
219
520
  ### 看甘特图
220
521
  ```bash
221
- python "$R/.qoder/scripts/team_sync.py" push
222
- python "$R/.qoder/scripts/task.py" gantt [--weeks 4]
522
+ python "$R/.qoder/scripts/domain/task/team_sync.py" push
523
+ python "$R/.qoder/scripts/orchestration/wlkj.py" task gantt [--weeks 4]
223
524
  ```
224
525
  - 只排有 `start_date`/`due_date` 的任务。
225
526
  - 没日期的任务 → 提示:"用 `set-due` 给任务加截止日期,甘特图才会显示。"
@@ -231,8 +532,8 @@ python "$R/.qoder/scripts/task.py" gantt [--weeks 4]
231
532
 
232
533
  ```bash
233
534
  # 把大任务 A 拆出子任务 B
234
- python "$R/.qoder/scripts/task.py" add-subtask <A任务名> <B任务名>
235
- python "$R/.qoder/scripts/task.py" remove-subtask <A任务名> <B任务名>
535
+ python "$R/.qoder/scripts/orchestration/wlkj.py" task add-subtask <A任务名> <B任务名>
536
+ python "$R/.qoder/scripts/orchestration/wlkj.py" task remove-subtask <A任务名> <B任务名>
236
537
  ```
237
538
  - 父任务记录 `children`,子任务记录 `parent`。
238
539
  - 产品用:把一个大需求拆成"前端/后端/数据"几个子任务,各自指派。
@@ -287,16 +588,18 @@ prd ──create(带REQ-ID)──→ task ──start──→ code/test ──f
287
588
 
288
589
  ```
289
590
  /wl-task ← list 给全貌(语义:看任务)
290
- /wl-task create <标题> ← 建任务(最高频)
591
+ /wl-task create <标题> ← 建任务(最高频)禅道可达时同步建禅道
291
592
  /wl-task list --ready ← 看能立刻做的
292
593
  /wl-task list --blocked ← 看卡住的
293
594
  /wl-task show <名> ← 看详情
294
- /wl-task start <名> ← 启动(移交开发)
595
+ /wl-task start <名> ← 启动(移交开发)禅道可达时同步 start_task
295
596
  /wl-task reassign <名> <开发> ← 改派任务负责人(PM 建完转给开发,多角色协作)
296
- /wl-task finish ← 完成
597
+ /wl-task finish ← 完成(禅道可达时同步 finish_task + 记工时)
297
598
  /wl-task archive <名> ← 归档
298
599
  /wl-task rank ← RICE 排优先级
299
600
  /wl-task plan ← 甘特图/排期/依赖
601
+ /wl-task zentao ← 查禅道侧任务/需求(禅道模式专属)
602
+ /wl-task sync ← 本地缓存 → 禅道(离线恢复后用)
300
603
  ```
301
604
 
302
605
  > 完整执行规则见 `.qoder/skills/wl-task/SKILL.md`(Quest/QoderWork 自然语言入口)。
@@ -18,7 +18,7 @@ User input: $ARGUMENTS
18
18
  ```bash
19
19
  R=$(python ~/.qoderwork/repo_root.py 2>/dev/null) || R=.
20
20
  ```
21
- > 后续脚本统一用 `python "$R/.qoder/scripts/xxx.py"`。
21
+ > 后续脚本统一用 `python "$R/.qoder/scripts/orchestration/wlkj.py" <命令>`。
22
22
 
23
23
  ## 🚦 第一步:路由(看用户输入,立刻决定走哪条线)
24
24
 
@@ -39,29 +39,55 @@ R=$(python ~/.qoderwork/repo_root.py 2>/dev/null) || R=.
39
39
  +该页面的通用交互模式生成用例。平台/环境 AI 推断(web 默认、test 默认),
40
40
  **只在真不确定平台时才问一句**。不要问地址、不要问登录方式——这些 config/文档里都有。
41
41
 
42
- ### 流程(3 步,不绕弯)
42
+ ### 🔴 quick 执行铁律(实测踩过的坑,违反 = 测试失败)
43
43
 
44
- **Step 1:AI 据用户描述生成用例 JSON。** 不搜代码。常见页面直接写:
45
- - 登录页:goto /auth/login 填账号密码 → 点登录 → 断言跳转
44
+ - **① 用意图式 intent+anchor 格式,禁止旧 action+CSS selector**(如 `input[placeholder*='账号']`)
45
+ - **② 占位符精确用 `{{ask:登录账号}}`/`{{ask:登录密码}}`**(匹配个人文档 key,别写"测试账号")
46
+ - **③ `--cases-file` 传文件,禁止内联 `--cases`**(长 JSON 截断)
47
+ - **④ recall 空时先 navigate+snapshot 拿真实 anchor 再填,禁止预猜 selector**
48
+ - **⑤ URL 写 `{{base_url}}/auth/login`(带斜杠,base_url 已去尾斜杠)**
49
+ - **⑥ recall 的 `--url` 用相对路径 `auth/login`**(前导斜杠会被 Windows Git-bash 转成 `/D:/.../Git/auth/login`)
50
+ - **⑦ Playwright 工具批量加载,snapshot 结果直接读,不写文件再 Read**
51
+ - **⑧ 验证码一气呵成(snapshot→截图→Read→type→点登录→删图),填完立刻提交**
52
+ - **⑨ 用例一次生成全部(正向+异常),别一条一条**
53
+
54
+ ### 流程(4 步,不绕弯)
55
+
56
+ **Step 0:recall 查锚点(用相对 URL)。**
57
+ ```bash
58
+ python "$R/.qoder/scripts/orchestration/wlkj.py" test recall --url auth/login
59
+ ```
60
+ - 有锚点 → 直接填进用例 anchor(零 LLM)
61
+ - 无锚点(首次)→ **先 navigate + snapshot 拿真实 a11y 树,从里面提取 anchor**,禁止预猜 selector
62
+
63
+ **Step 1:AI 据用户描述 + 锚点生成用例 JSON。** 不搜代码。常见页面直接写:
64
+ - 登录页:goto `{{base_url}}/auth/login` → 填账号密码 → 点登录 → 断言跳转
46
65
  - 列表页:goto 列表 → 断言表格加载 → 翻页/筛选
47
66
  - 表单页:goto → 填字段 → 提交 → 断言成功提示
48
- 需要真实数据(账号/密码)用 `{{ask:描述}}` 占位,URL 用 `{{base_url}}`。
49
- 正向 + 关键异常各一条。
67
+ 需要真实数据(账号/密码)用 `{{ask:描述}}` 占位,URL 用 `{{base_url}}/xxx`(**带斜杠**)。
68
+ 正向 + 关键异常各一条。**一次生成全部用例。**
69
+ ```json
70
+ // ✅ 意图式格式 (anchor 来自 recall 或 snapshot)
71
+ {"intent":"fill","desc":"账号框","value":"{{ask:登录账号}}","anchor":{"role":"textbox","name":"尊敬的管理员"}}
72
+ // ❌ 禁止: 旧 action+CSS selector
73
+ {"action":"fill","target":"input[placeholder*='账号']","value":"{{ask:测试账号}}"}
74
+ ```
50
75
 
51
76
  **Step 2:调脚本,注入数据 + 出执行计划:**
52
77
  ```bash
53
- python "$R/.qoder/scripts/autotest.py" quick \
54
- --cases '<Step1生成的JSON>' \
78
+ # AI 先把用例 JSON 写文件, 再传路径 (--cases-file, 不截断)
79
+ python "$R/.qoder/scripts/orchestration/wlkj.py" test quick \
80
+ --cases-file <文件路径> \
55
81
  --desc "<用户描述>" --platform <web|app>
56
82
  ```
57
83
  脚本自动:解析 test 环境域名 + 从个人文档注入账号密码 + 出执行计划。
58
84
  若提示"需要测试数据" → 逐项问用户,再用 `--data` 传。
59
85
 
60
- **Step 3:执行由 QoderWork 浏览器连接器承担(真实场景)。**
61
- 脚本只出计划,不自己开浏览器。无连接器 降级人工核对清单。
86
+ **Step 3:执行由 Playwright MCP 驱动(真实场景)。**
87
+ 脚本只出计划,不自己开浏览器。批量加载 browser_* 工具后按执行计划连发操作。
62
88
  跑完回传结果出报告:
63
89
  ```bash
64
- python "$R/.qoder/scripts/autotest.py" quick --record '{"Q-1":"pass","Q-2":"fail"}'
90
+ python "$R/.qoder/scripts/orchestration/wlkj.py" test quick --record '{"Q-1":"pass","Q-2":"fail"}'
65
91
  ```
66
92
 
67
93
  ---
@@ -71,11 +97,11 @@ python "$R/.qoder/scripts/autotest.py" quick --record '{"Q-1":"pass","Q-2":"fail
71
97
  用户明确给了任务名/回归某个任务时走这条。完整流程见 skill。
72
98
 
73
99
  ```bash
74
- python "$R/.qoder/scripts/task.py" list # 确认任务
75
- python "$R/.qoder/scripts/autotest.py" generate <task> # 生成骨架
100
+ cap.mcp.call("list_tasks", {}) # 确认任务
101
+ python "$R/.qoder/scripts/orchestration/wlkj.py" test generate <task> # 生成骨架
76
102
  # AI 读任务 PRD「验收标准」补全 steps/expected(真实数据用 {{ask:}})
77
- python "$R/.qoder/scripts/autotest.py" run <task> # 注入+出执行计划
78
- python "$R/.qoder/scripts/autotest.py" run <task> --record '{"..":"pass"}' # 回收结果
103
+ python "$R/.qoder/scripts/orchestration/wlkj.py" test run <task> # 注入+出执行计划
104
+ python "$R/.qoder/scripts/orchestration/wlkj.py" test run <task> --record '{"..":"pass"}' # 回收结果
79
105
  ```
80
106
 
81
107
  ---
@@ -84,9 +110,9 @@ python "$R/.qoder/scripts/autotest.py" run <task> --record '{"..":"pass"}' #
84
110
 
85
111
  用户说"测试覆盖""哪些没测""回归盲区"时走这条。
86
112
 
87
- **PREFERRED: MCP 工具(最快):**
113
+ **调用方式:**
88
114
  ```
89
- mcp__coverage_matrix()
115
+ cap.mcp.call("coverage_matrix", {})
90
116
  ```
91
117
 
92
118
  **FALLBACK: 直接看索引:**