@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.
- package/bin/cli.js +1435 -1107
- package/package.json +29 -29
- package/templates/qoder/agents/insight-planning.md +1 -1
- package/templates/qoder/agents/insight-research.md +28 -15
- package/templates/qoder/commands/optional/wl-insight.md +161 -161
- package/templates/qoder/commands/optional/wl-report.md +20 -39
- package/templates/qoder/commands/optional/wl-status.md +4 -4
- package/templates/qoder/commands/wl-code.md +2 -2
- package/templates/qoder/commands/wl-design.md +6 -6
- package/templates/qoder/commands/wl-init.md +3 -3
- package/templates/qoder/commands/wl-prd.md +26 -13
- package/templates/qoder/commands/wl-req.md +43 -0
- package/templates/qoder/commands/wl-search.md +78 -47
- package/templates/qoder/commands/wl-task.md +343 -40
- package/templates/qoder/commands/wl-test.md +43 -17
- package/templates/qoder/config.yaml +52 -14
- package/templates/qoder/hooks/post-tool-use.py +181 -0
- package/templates/qoder/hooks/session-start.py +94 -23
- package/templates/qoder/hooks/stop-eval.py +207 -0
- package/templates/qoder/hooks/user-prompt-submit.py +139 -0
- package/templates/qoder/rules/wl-pipeline.md +85 -49
- package/templates/qoder/scripts/README.md +102 -0
- package/templates/qoder/scripts/capability/__init__.py +26 -0
- package/templates/qoder/scripts/capability/__main__.py +72 -0
- package/templates/qoder/scripts/capability/adapters/__init__.py +29 -0
- package/templates/qoder/scripts/capability/adapters/cli.py +316 -0
- package/templates/qoder/scripts/capability/adapters/mcp.py +334 -0
- package/templates/qoder/scripts/capability/adapters/qw.py +271 -0
- package/templates/qoder/scripts/capability/caps/__init__.py +27 -0
- package/templates/qoder/scripts/capability/caps/context.py +36 -0
- package/templates/qoder/scripts/capability/caps/cron.py +50 -0
- package/templates/qoder/scripts/capability/caps/identity.py +43 -0
- package/templates/qoder/scripts/capability/caps/memory.py +71 -0
- package/templates/qoder/scripts/capability/caps/notify.py +41 -0
- package/templates/qoder/scripts/capability/caps/present.py +48 -0
- package/templates/qoder/scripts/capability/caps/repo.py +31 -0
- package/templates/qoder/scripts/capability/caps/sandbox.py +43 -0
- package/templates/qoder/scripts/capability/chain.py +92 -0
- package/templates/qoder/scripts/capability/memory_chain.py +41 -0
- package/templates/qoder/scripts/capability/registry.py +246 -0
- package/templates/qoder/scripts/capability/registry_mcp.py +250 -0
- package/templates/qoder/scripts/capability/smoke_test.py +211 -0
- package/templates/qoder/scripts/capability/smoke_test_report.json +94 -0
- package/templates/qoder/scripts/deployment/setup/__init__.py +11 -0
- package/templates/qoder/scripts/deployment/setup/carriers.py +669 -0
- package/templates/qoder/scripts/deployment/setup/carriers_verify.py +148 -0
- package/templates/qoder/scripts/{init_doctor.py → deployment/setup/init_doctor.py} +100 -39
- package/templates/qoder/scripts/{install_qoderwork.py → deployment/setup/install_qoderwork.py} +48 -30
- package/templates/qoder/scripts/{platform_doctor.py → deployment/setup/platform_doctor.py} +271 -259
- package/templates/qoder/scripts/{repo_root.py → deployment/setup/repo_root.py} +9 -2
- package/templates/qoder/scripts/{setup.py → deployment/setup/setup.py} +143 -14
- package/templates/qoder/scripts/{setup_lanhu.py → deployment/setup/setup_lanhu.py} +979 -963
- package/templates/qoder/scripts/domain/design/__init__.py +0 -0
- package/templates/qoder/scripts/{fill_prototype.py → domain/design/fill_prototype.py} +17 -5
- package/templates/qoder/scripts/{gen_design_doc.py → domain/design/gen_design_doc.py} +406 -394
- package/templates/qoder/scripts/domain/kg/__init__.py +11 -0
- package/templates/qoder/scripts/domain/kg/build/__init__.py +0 -0
- package/templates/qoder/scripts/domain/kg/build/build_entity_registry.py +201 -0
- package/templates/qoder/scripts/domain/kg/build/build_relations.py +132 -0
- package/templates/qoder/scripts/{build_style_index.py → domain/kg/build/build_style_index.py} +50 -15
- package/templates/qoder/scripts/domain/kg/build/build_workflows.py +149 -0
- package/templates/qoder/scripts/{kg_build.py → domain/kg/build/kg_build.py} +685 -612
- package/templates/qoder/scripts/{kg_build_db.py → domain/kg/build/kg_build_db.py} +338 -327
- package/templates/qoder/scripts/{kg_incremental.py → domain/kg/build/kg_incremental.py} +425 -393
- package/templates/qoder/scripts/{learn_aggregate.py → domain/kg/build/learn_aggregate.py} +212 -201
- package/templates/qoder/scripts/domain/kg/extract/__init__.py +0 -0
- package/templates/qoder/scripts/{common → domain/kg/extract}/extract.py +430 -419
- package/templates/qoder/scripts/domain/kg/extract/test_extract.py +115 -0
- package/templates/qoder/scripts/{common → domain/kg/extract}/ts_extract.py +614 -536
- package/templates/qoder/scripts/domain/kg/graph/__init__.py +0 -0
- package/templates/qoder/scripts/{kg_link_db.py → domain/kg/graph/kg_link_db.py} +235 -224
- package/templates/qoder/scripts/domain/kg/graph/kg_semantic.py +683 -0
- package/templates/qoder/scripts/{kg.py → domain/kg/kg.py} +871 -708
- package/templates/qoder/scripts/domain/kg/kg_capabilities.py +182 -0
- package/templates/qoder/scripts/domain/kg/search/__init__.py +0 -0
- package/templates/qoder/scripts/{context_pack.py → domain/kg/search/context_pack.py} +45 -17
- package/templates/qoder/scripts/{enrich_prompt.py → domain/kg/search/enrich_prompt.py} +238 -226
- package/templates/qoder/scripts/domain/kg/search/prefetch.py +384 -0
- package/templates/qoder/scripts/{common → domain/kg/search}/search_engine.py +207 -205
- package/templates/qoder/scripts/{search_index.py → domain/kg/search/search_index.py} +210 -88
- package/templates/qoder/scripts/domain/kg/server/__init__.py +0 -0
- package/templates/qoder/scripts/domain/kg/server/kgd.py +549 -0
- package/templates/qoder/scripts/domain/kg/server/perf_bench.py +197 -0
- package/templates/qoder/scripts/domain/kg/storage/__init__.py +0 -0
- package/templates/qoder/scripts/{kg_duckdb.py → domain/kg/storage/kg_duckdb.py} +70 -45
- package/templates/qoder/scripts/domain/learning/__init__.py +0 -0
- package/templates/qoder/scripts/{learn.py → domain/learning/learn.py} +157 -146
- package/templates/qoder/scripts/domain/report/__init__.py +11 -0
- package/templates/qoder/scripts/{add_session.py → domain/report/add_session.py} +256 -244
- package/templates/qoder/scripts/{export.py → domain/report/export.py} +72 -4
- package/templates/qoder/scripts/{report.py → domain/report/report.py} +292 -281
- package/templates/qoder/scripts/domain/report/report_snapshot.py +261 -0
- package/templates/qoder/scripts/domain/report/role.py +39 -0
- package/templates/qoder/scripts/{status.py → domain/report/status.py} +737 -628
- package/templates/qoder/scripts/domain/report/status_snapshot.py +191 -0
- package/templates/qoder/scripts/domain/requirement/__init__.py +0 -0
- package/templates/qoder/scripts/{archive_prd.py → domain/requirement/archive_prd.py} +389 -377
- package/templates/qoder/scripts/domain/requirement/req.py +228 -0
- package/templates/qoder/scripts/domain/task/__init__.py +11 -0
- package/templates/qoder/scripts/{git_sync.py → domain/task/git_sync.py} +90 -50
- package/templates/qoder/scripts/{syncgate.py → domain/task/syncgate.py} +18 -7
- package/templates/qoder/scripts/domain/task/task.py +229 -0
- package/templates/qoder/scripts/domain/task/task_lifecycle.py +606 -0
- package/templates/qoder/scripts/domain/task/task_query.py +162 -0
- package/templates/qoder/scripts/domain/task/task_relations.py +425 -0
- package/templates/qoder/scripts/{team_sync.py → domain/task/team_sync.py} +101 -23
- package/templates/qoder/scripts/domain/task/zentao_sync.py +370 -0
- package/templates/qoder/scripts/foundation/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/bootstrap.py +145 -0
- package/templates/qoder/scripts/foundation/core/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/core/cmd_registry.py +112 -0
- package/templates/qoder/scripts/foundation/core/config.py +187 -0
- package/templates/qoder/scripts/{common → foundation/core}/paths.py +706 -400
- package/templates/qoder/scripts/foundation/data/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/data/contract.py +317 -0
- package/templates/qoder/scripts/{common → foundation/data}/events.py +2 -2
- package/templates/qoder/scripts/foundation/data/result.py +223 -0
- package/templates/qoder/scripts/{common → foundation/data}/task_utils.py +427 -392
- package/templates/qoder/scripts/foundation/identity/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/identity/check_publish.py +103 -0
- package/templates/qoder/scripts/{common → foundation/identity}/developer.py +239 -238
- package/templates/qoder/scripts/foundation/identity/guard.py +159 -0
- package/templates/qoder/scripts/{common → foundation/identity}/identity.py +132 -9
- package/templates/qoder/scripts/foundation/identity/roles.py +60 -0
- package/templates/qoder/scripts/foundation/integrations/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/integrations}/active_task.py +30 -15
- package/templates/qoder/scripts/{common → foundation/integrations}/feishu.py +11 -10
- package/templates/qoder/scripts/{common → foundation/integrations}/terms.py +3 -3
- package/templates/qoder/scripts/foundation/integrations/zentao_client.py +220 -0
- package/templates/qoder/scripts/foundation/io/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/io}/filelock.py +1 -1
- package/templates/qoder/scripts/{common → foundation/io}/reqid.py +1 -1
- package/templates/qoder/scripts/foundation/protocol/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/protocol/duckdb_conn.py +39 -0
- package/templates/qoder/scripts/foundation/protocol/mcp_base.py +268 -0
- package/templates/qoder/scripts/foundation/utils/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/utils}/eval_api.py +12 -1
- package/templates/qoder/scripts/{common → foundation/utils}/pip_install.py +23 -6
- package/templates/qoder/scripts/{common → foundation/utils}/platform_guard.py +1 -1
- package/templates/qoder/scripts/orchestration/__init__.py +0 -0
- package/templates/qoder/scripts/orchestration/wlkj.py +185 -0
- package/templates/qoder/scripts/protocol/__init__.py +0 -0
- package/templates/qoder/scripts/protocol/browser/README.md +207 -0
- package/templates/qoder/scripts/protocol/browser/SKILL.md +265 -0
- package/templates/qoder/scripts/protocol/browser/config.env +9 -0
- package/templates/qoder/scripts/protocol/browser/references/cdp-api.md +110 -0
- package/templates/qoder/scripts/protocol/browser/references/migration-2.5.3.md +72 -0
- package/templates/qoder/scripts/protocol/browser/references/site-patterns/.gitkeep +0 -0
- package/templates/qoder/scripts/protocol/browser/scripts/browser-discovery.mjs +138 -0
- package/templates/qoder/scripts/protocol/browser/scripts/cdp-proxy.mjs +672 -0
- package/templates/qoder/scripts/protocol/browser/scripts/check-deps.mjs +206 -0
- package/templates/qoder/scripts/protocol/browser/scripts/find-url.mjs +253 -0
- package/templates/qoder/scripts/protocol/browser/scripts/match-site.mjs +46 -0
- package/templates/qoder/scripts/protocol/browser/templates/config.env.template +9 -0
- package/templates/qoder/scripts/protocol/mcp/__init__.py +11 -0
- package/templates/qoder/scripts/protocol/mcp/kg_mcp_server.py +478 -0
- package/templates/qoder/scripts/{lanhu_stdio_wrapper.py → protocol/mcp/lanhu_stdio_wrapper.py} +131 -119
- package/templates/qoder/scripts/protocol/mcp/mcp_doctor.py +548 -0
- package/templates/qoder/scripts/protocol/mcp/mcp_launcher.py +600 -0
- package/templates/qoder/scripts/protocol/mcp/mysql_mcp_server.py +461 -0
- package/templates/qoder/scripts/protocol/mcp/zentao_mcp_server.py +1920 -0
- package/templates/qoder/scripts/protocol/transports/__init__.py +56 -0
- package/templates/qoder/scripts/protocol/transports/base.py +87 -0
- package/templates/qoder/scripts/protocol/transports/cli.py +132 -0
- package/templates/qoder/scripts/protocol/transports/http.py +141 -0
- package/templates/qoder/scripts/protocol/transports/stdio.py +293 -0
- package/templates/qoder/scripts/run_weekly_update.bat +27 -18
- package/templates/qoder/scripts/run_weekly_update.sh +22 -13
- package/templates/qoder/scripts/validation/__init__.py +0 -0
- package/templates/qoder/scripts/validation/eval/__init__.py +0 -0
- package/templates/qoder/scripts/validation/eval/perf-report-2026-06-24.md +139 -0
- package/templates/qoder/scripts/validation/eval/qwork_harness.py +617 -0
- package/templates/qoder/scripts/validation/eval/report-commands.md +220 -0
- package/templates/qoder/scripts/validation/eval/transcript_timing.py +386 -0
- package/templates/qoder/scripts/validation/metrics/__init__.py +0 -0
- package/templates/qoder/scripts/{eval_prd.py → validation/metrics/eval_prd.py} +84 -15
- package/templates/qoder/scripts/validation/metrics/usability_score.py +750 -0
- package/templates/qoder/scripts/validation/test/__init__.py +11 -0
- package/templates/qoder/scripts/validation/test/assertion_gen.py +551 -0
- package/templates/qoder/scripts/{autotest.py → validation/test/autotest.py} +1234 -1751
- package/templates/qoder/scripts/validation/test/autotest_auth.py +109 -0
- package/templates/qoder/scripts/{autotest_batch.py → validation/test/autotest_batch.py} +255 -224
- package/templates/qoder/scripts/validation/test/autotest_data.py +680 -0
- package/templates/qoder/scripts/validation/test/autotest_opencli.py +646 -0
- package/templates/qoder/scripts/{autotest_run.py → validation/test/autotest_run.py} +323 -297
- package/templates/qoder/scripts/validation/test/autotest_webaccess.py +571 -0
- package/templates/qoder/scripts/{benchmark.py → validation/test/benchmark.py} +17 -5
- package/templates/qoder/scripts/{kg_auto_login.py → validation/test/kg_auto_login.py} +208 -196
- package/templates/qoder/scripts/{kg_test_runner.py → validation/test/kg_test_runner.py} +13 -2
- package/templates/qoder/scripts/{page_probe.py → validation/test/page_probe.py} +470 -459
- package/templates/qoder/scripts/validation/test/smoke_all_commands.py +170 -0
- package/templates/qoder/settings.json +23 -2
- package/templates/qoder/skills/design-import/SKILL.md +235 -226
- package/templates/qoder/skills/design-review/SKILL.md +91 -82
- package/templates/qoder/skills/prd-generator/SKILL.md +38 -24
- package/templates/qoder/skills/prd-review/SKILL.md +8 -6
- package/templates/qoder/skills/prototype-generator/SKILL.md +296 -247
- package/templates/qoder/skills/spec-coder/SKILL.md +24 -15
- package/templates/qoder/skills/spec-generator/SKILL.md +24 -12
- package/templates/qoder/skills/test-generator/SKILL.md +9 -7
- package/templates/qoder/skills/wl-code/SKILL.md +25 -13
- package/templates/qoder/skills/wl-commit/SKILL.md +10 -9
- package/templates/qoder/skills/wl-design/SKILL.md +7 -5
- package/templates/qoder/skills/wl-init/SKILL.md +8 -8
- package/templates/qoder/skills/wl-insight/SKILL.md +25 -12
- package/templates/qoder/skills/wl-prd-full/SKILL.md +59 -8
- package/templates/qoder/skills/wl-prd-quick/SKILL.md +7 -7
- package/templates/qoder/skills/wl-prd-review/SKILL.md +18 -6
- package/templates/qoder/skills/wl-report/SKILL.md +27 -25
- package/templates/qoder/skills/wl-search/SKILL.md +151 -80
- package/templates/qoder/skills/wl-spec/SKILL.md +9 -7
- package/templates/qoder/skills/wl-status/SKILL.md +50 -18
- package/templates/qoder/skills/wl-task/SKILL.md +94 -12
- package/templates/qoder/skills/wl-test/SKILL.md +154 -45
- package/templates/qoder/templates/prd-full-template.md +7 -0
- package/templates/root/AGENTS.md +275 -237
- package/templates/root/requirements.txt +3 -0
- package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +3 -3
- package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +2 -2
- package/templates/qoder/hooks/inject-workflow-state.py +0 -169
- package/templates/qoder/scripts/__pycache__/check_mcp_launch.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/install_qoderwork.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/mcp_launcher.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/platform_doctor.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/check_carriers.py +0 -238
- package/templates/qoder/scripts/check_mcp.py +0 -298
- package/templates/qoder/scripts/check_mcp_launch.py +0 -183
- package/templates/qoder/scripts/check_qoderwork_consistency.py +0 -166
- package/templates/qoder/scripts/collect_prds.py +0 -31
- package/templates/qoder/scripts/common/mentions.py +0 -134
- package/templates/qoder/scripts/common/utf8.py +0 -38
- package/templates/qoder/scripts/extract_api_params.py +0 -246
- package/templates/qoder/scripts/extract_routes.py +0 -54
- package/templates/qoder/scripts/extract_routes_tree.py +0 -78
- package/templates/qoder/scripts/handoff.py +0 -22
- package/templates/qoder/scripts/init_developer.py +0 -76
- package/templates/qoder/scripts/kg_mcp_server.py +0 -801
- package/templates/qoder/scripts/kg_semantic.py +0 -150
- package/templates/qoder/scripts/mcp_launcher.py +0 -414
- package/templates/qoder/scripts/mysql_mcp_server.py +0 -396
- package/templates/qoder/scripts/parse_prds.py +0 -33
- package/templates/qoder/scripts/role.py +0 -51
- package/templates/qoder/scripts/sync_carriers.py +0 -259
- package/templates/qoder/scripts/task.py +0 -1261
- package/templates/qoder/scripts/workspace_init.py +0 -102
- package/templates/qoder/scripts/zentao_mcp_server.py +0 -424
- package/templates/qoder/skills/prompt-enrich/SKILL.md +0 -90
- /package/templates/qoder/scripts/{common → deployment}/__init__.py +0 -0
- /package/templates/qoder/{skills/prototype-generator/SKILL.md.zcode-79180-2af4721f-f9a6-412c-88db-c0af680d211b.tmp → scripts/domain/__init__.py} +0 -0
- /package/templates/qoder/scripts/{common → domain/kg/graph}/graph_traverse.py +0 -0
- /package/templates/qoder/scripts/{common → domain/kg/server}/repowiki.py +0 -0
- /package/templates/qoder/scripts/{common → foundation/io}/atomicio.py +0 -0
- /package/templates/qoder/scripts/{secure-ls.js → validation/test/secure-ls.js} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: wl-task
|
|
3
|
-
description: "任务管理 + RICE 优先级 + 依赖排期。7 站流水线的'任务'站,产品在这里把 PRD 落成任务。Task management with RICE prioritization.
|
|
4
|
-
trigger: "
|
|
3
|
+
description: "任务管理 + RICE 优先级 + 依赖排期。7 站流水线的'任务'站,产品在这里把 PRD 落成任务。Task management with RICE prioritization. "
|
|
4
|
+
trigger: "user invokes /wl-* command explicitly"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
|
|
@@ -11,7 +11,7 @@ trigger: "用户说'建任务''建个任务''排优先级''开始做''做完了'
|
|
|
11
11
|
```bash
|
|
12
12
|
R=$(python ~/.qoderwork/repo_root.py 2>/dev/null) || R=.
|
|
13
13
|
```
|
|
14
|
-
> `repo_root.py` 从 `~/.qoderwork/mcp.json` 反推仓库根;失败回退 `.`(IDE/CLI 工作目录即仓库根)。找不到时先跑 `python
|
|
14
|
+
> `repo_root.py` 从 `~/.qoderwork/mcp.json` 反推仓库根;失败回退 `.`(IDE/CLI 工作目录即仓库根)。找不到时先跑 `python "$R/.qoder/scripts/orchestration/wlkj.py" install-qw`。
|
|
15
15
|
|
|
16
16
|
# wl-task — 任务管理(7 站之「任务」站 · 产品工位)
|
|
17
17
|
|
|
@@ -25,8 +25,10 @@ R=$(python ~/.qoderwork/repo_root.py 2>/dev/null) || R=.
|
|
|
25
25
|
| 用户说什么 | 动作 | 跑什么 |
|
|
26
26
|
|-----------|------|--------|
|
|
27
27
|
| "建个任务 XXX" / "把刚才需求建成任务" / "create" | **create** | `task.py create`(带 PRD 衔接) |
|
|
28
|
-
| "
|
|
29
|
-
| "
|
|
28
|
+
| "列一下**本地**任务" / "有哪些任务" / "list" | **list** | `task.py list`(本地离线缓存,非禅道) |
|
|
29
|
+
| **"我的任务" / "我的禅道" / "这周干啥" / "我的Bug"** | **自然查询** | ★直接 `my_workbench()`(见下方禅道查询总表),不走本命令工序 |
|
|
30
|
+
| **"看下#301" / "需求#761怎么样了" / "Bug状态"** | **自然查询** | 直接 `get_task` / `get_story_detail` / `get_bug`,自然回答 |
|
|
31
|
+
| "接下来做啥" / "能做的有哪些" | **list --ready** | 只看能立刻开始的(本地依赖视角) |
|
|
30
32
|
| "卡住的有哪些" / "哪些被阻塞" | **list --blocked** | 只看有未完成依赖的 |
|
|
31
33
|
| "看下 XXX 任务" / "XXX 怎么样了" / "show" | **show** | `task.py show <名>` |
|
|
32
34
|
| "开始做 XXX" / "启动 XXX" / "start" | **start** | `task.py start <名>` |
|
|
@@ -36,16 +38,96 @@ R=$(python ~/.qoderwork/repo_root.py 2>/dev/null) || R=.
|
|
|
36
38
|
| "排期" / "甘特图" / "排个日期" / "plan" | **plan** | `task.py gantt` + set-due |
|
|
37
39
|
| 模糊 / 拿不准 | 先 list 给全貌 | 再问要操作哪个 |
|
|
38
40
|
|
|
41
|
+
> **边界**:上面"自然查询"行的(我的任务/看进度/Bug状态)—— AI 直接用禅道 MCP 工具回答,**不拉入工序流程**。
|
|
42
|
+
> "PRD发禅道/记工时/改状态"这类**显式操作**才走工序(见 wl-task.md 的 zentao 剧本)。
|
|
43
|
+
|
|
44
|
+
### 🔌 宿主路由:禅道怎么调(看你在哪个宿主)
|
|
45
|
+
|
|
46
|
+
> ★ 丝滑关键。禅道工具有两条入口,用错就慢/重复 spawn/报错。
|
|
47
|
+
|
|
48
|
+
| 宿主 | 调用入口 | 何时用 |
|
|
49
|
+
|------|---------|--------|
|
|
50
|
+
| **QoderWork** | `qw_mcp_call("工具名", {参数})` | 第一宿主。QoderWork 已起 zentao 进程,直接复用,**省 list/get 两步元调用**。下表工具名确定时直接 call。 |
|
|
51
|
+
| **Qoder IDE / Quest** | `mcp__qoder-zentao__工具名(参数)` | IDE 把 MCP 工具暴露成 `mcp__<server>__<tool>`。 |
|
|
52
|
+
| **CLI / 其他** | `cap.mcp.call("工具名", {参数})` | 无原生 MCP,capability 层 spawn 本地 server。 |
|
|
53
|
+
|
|
54
|
+
**判定宿主**:`QODER_SESSION_ID` 存在 = QoderWork;否则 `detect_host()`。下表统一用 `qw_mcp_call(...)` 示意(CLI 换 `cap.mcp.call`,参数一致)。
|
|
55
|
+
|
|
56
|
+
### ★★★ 禅道查询总表 + 三铁律(处理任何禅道意图,先查这张表,禁止瞎试)
|
|
57
|
+
|
|
58
|
+
> 这三轮实测的病根:AI 看到"看我的禅道X"找不到路就**瞎试→枚举→调用爆炸/token浪费**。
|
|
59
|
+
> 下面这张表是"宪法"——任何禅道意图,**先匹配表里的行,照着走**。匹配不上再问用户,**绝不逐个枚举**。
|
|
60
|
+
|
|
61
|
+
#### 🔒 三条铁律(违反 = 失败,AI 必读)
|
|
62
|
+
|
|
63
|
+
1. **禁止枚举扇出**:拿到 list 结果后,**绝不**逐个调 `get_*_detail`。list 返回的 `id+name+status` 已够用;只有用户明确点了某条("看#761详情"),才单独 `get_story_detail(story_id=761)` 一次。
|
|
64
|
+
2. **🚫 跨域不冒充(v3.1.5 新增,最重要)**:用户问「任务」就查 task 域,问「需求」查 story 域,问「Bug」查 bug 域。**查不到如实说"没有指派给你的任务"+贴查询证据,绝不拿需求/Bug 顶替任务来回答**。用户问"我的任务"却收到需求列表 = 严重失败。
|
|
65
|
+
3. **写操作要确认**:改/删需求、执行、版本这类**无归属校验**的工具,调之前先把要操作的项列出来让用户确认(防改错队友/全局资源)。任务/Bug 的 start/finish/resolve 等有 `_assert_owner` 兜底,但仍建议先 get 一次确认是我的。
|
|
66
|
+
|
|
67
|
+
#### 意图路由总表(用户说什么 → 调什么 → 调几次)
|
|
68
|
+
|
|
69
|
+
| 用户说的意图 | 直达工具 | 调用次数 | 关键约束 |
|
|
70
|
+
|------------|---------|:---:|---------|
|
|
71
|
+
| **看我的禅道任务/这周干啥/我手头有啥** | ★`my_workbench()` | **1** | 跨执行/产品全量聚合,一行拿全!不再逐执行查 |
|
|
72
|
+
| 看我未开始/进行中的任务 | `list_my_tasks(status='wait')` / `list_my_tasks(status='doing')` | 1 | 跨执行全量 |
|
|
73
|
+
| **看我的禅道需求** / "我的需求" | `my_workbench()` 看需求段(或逐产品 `list_stories`) | 1 | 🚫**禁逐个 get_story_detail** |
|
|
74
|
+
| **看我的Bug** | ★`list_my_bugs()` | **1** | 跨产品全量,不再逐产品查 |
|
|
75
|
+
| 看某执行任务(已知eid) | `list_execution_tasks(eid)` | 1 | 默认只看我的 |
|
|
76
|
+
| 看全队任务/需求/Bug | 对应工具 + `all=true` | 1 | ★**必须用户明说"全队"才加** |
|
|
77
|
+
| 看某条详情("#761怎么样") | `get_story_detail` / `get_task` / `get_bug` (该 id) | 1 | 用户**点了具体 ID** 才调 |
|
|
78
|
+
| 看有哪些产品/执行/版本 | `list_products` / `list_executions` / `list_builds(project_id=9)` | 1 | 导航类,可自由调(公开结构) |
|
|
79
|
+
| 建需求 / PRD发禅道 | `create_story` → 然后**主动问关联版本**(见下) | 1+ | 不知产品先 `list_products` 勾选 |
|
|
80
|
+
| 关联版本 | `list_builds(project_id=9)` → `link_story_to_build` | 2 | 多选 = 一个需求进当前+未来版本 |
|
|
81
|
+
| 拆需求为任务 | `list_executions` 选 → `create_task` | 2 | 不填 assignedTo 默认指派给我 |
|
|
82
|
+
| 开始/完成/记工时任务 | `start_task` / `finish_task` / `log_effort` (task_id) | 1 | 有归属校验;finish 要问消耗工时 |
|
|
83
|
+
|
|
84
|
+
#### 「看我的工作」统一范式(v3.1.5 升级)
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
★ 首选: my_workbench() —— 一次返回任务+需求+Bug 三域全量聚合, 跨所有执行/产品不漏。
|
|
88
|
+
这是回答"我手头有什么活/我的任务/我的禅道"的默认工具, 调用次数从原来的 N(逐执行) 降到 1。
|
|
89
|
+
|
|
90
|
+
单域查询: list_my_tasks(status=...) / list_my_bugs() —— 跨执行/产品全量, 只看我的。
|
|
91
|
+
|
|
92
|
+
旧的逐执行/逐产品查法 (list_execution_tasks 逐个执行、list_stories 逐个产品) 仍可用,
|
|
93
|
+
但只用于"已知具体执行/产品ID"的场景。查"我的全部"会被漏数据(实测 61 执行逐个查会漏)。
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
#### 归属账号未识别(v3.1.5 起工具层直接拒绝)
|
|
97
|
+
|
|
98
|
+
若 `my_workbench` / `list_my_tasks` / `list_my_bugs` / `list_execution_tasks`(默认) 返回:
|
|
99
|
+
`🚫 未识别禅道身份: member.json 缺 zentao_account...`
|
|
100
|
+
→ 这是**工具层主动拒绝**(不再静默降级)。原样把提示转述给用户,引导跑 `/wl-init` 配置。
|
|
101
|
+
**绝不绕过**(不传 all=true 去看全队当我的,不拿别的域顶替)。配好后重启 MCP 生效。
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
#### 建需求后主动问版本(★ 你们的版本=项目级 build)
|
|
105
|
+
|
|
106
|
+
> 你们的「版本」是**项目级 build**(项目#9 下按周 `V+日期`,如 `V20260625`),不是产品级 plan/release。一个需求可能**同时进当前+未来版本**,所以**显式让 PM 多选**,不自动关联。
|
|
107
|
+
|
|
108
|
+
建需求/评审通过后,AI **主动**列最近 build 让 PM 勾选:
|
|
109
|
+
```
|
|
110
|
+
AI: 要关联到哪个版本?(最近 6 个 build):
|
|
111
|
+
[1] #22 V20260625 (本周) [2] #21 V20260618 (上周) [3] #16 V20260604 ...
|
|
112
|
+
PM> 1,3 ← 多选 = 一个需求进当前版本(本周发)+未来版本
|
|
113
|
+
AI: ✅ 需求#761 已关联 build#22(本周) + #16(未来)
|
|
114
|
+
```
|
|
115
|
+
- 候选:`list_builds(project_id=9)` 取最近 6 个,按时间倒序编号。
|
|
116
|
+
- 关联:`link_story_to_build(build_id=<选中>, stories=[<需求ID>])`,**逐个 build 调一次**。
|
|
117
|
+
- PM 也可主动触发:"把#761 关联到本周版本" → AI 查本周 build 再关联。
|
|
118
|
+
|
|
39
119
|
## ⚙️ 自取上下文(Quest / QoderWork 无 hook 注入,必须自读)
|
|
40
120
|
|
|
41
|
-
-
|
|
42
|
-
|
|
43
|
-
|
|
121
|
+
- **当前身份**:当前开发者(assignee 默认值)。读法:先 `R=$(python ~/.qoderwork/repo_root.py 2>/dev/null) || R=.` 定位仓库根,
|
|
122
|
+
再 `python "$R/.qoder/scripts/foundation/core/paths.py" whoami`(输出开发者名;空=未初始化)。
|
|
123
|
+
⚠️ 勿用裸相对路径 `.qoder/.developer`(QoderWork 桌面端 cwd 不在仓库根,会读不到而误报未初始化)。
|
|
124
|
+
- **活动任务**:当前活动任务("开始做""完成了"默认对它操作)。读法 `python "$R/.qoder/scripts/foundation/core/paths.py" current-task`(输出任务路径;空=无活动任务)。
|
|
125
|
+
- 列出已有任务:`cap.mcp.call("list_tasks", {})`
|
|
44
126
|
- 衔接 PRD:扫 `workspace/members/{dev}/drafts/REQ-*.md` + `data/docs/prd/REQ-*.md`
|
|
45
127
|
|
|
46
128
|
## 🔒 三道前置检查(任何动作前先做,保证健壮)
|
|
47
129
|
|
|
48
|
-
1.
|
|
130
|
+
1. **身份检查**:跑 `python "$R/.qoder/scripts/foundation/core/paths.py" whoami`,无输出/退出码1 = 未初始化 → 停下,提示先 `/wl-init`(否则 create 会把 assignee 写空)。
|
|
49
131
|
2. **空数据兜底**:tasks 目录不存在/空 → 读操作友好提示"还没有任务",不报错;create 直接建。
|
|
50
132
|
3. **脚本退出码**:`task.py` 返回非 0 → 原样读 stderr 告诉用户。`4`=权限不足(零信任 ACL),`1`=任务不存在/JSON 缺失。**绝不绕过 ACL。**
|
|
51
133
|
|
|
@@ -56,7 +138,7 @@ R=$(python ~/.qoderwork/repo_root.py 2>/dev/null) || R=.
|
|
|
56
138
|
**create(最高频)—— PRD 衔接不丢字段:**
|
|
57
139
|
- 有 PRD → 从 PRD 提取标题/模块/验收点,**别让用户重述**;把 REQ-ID 记进 task.json 的 `tags`。
|
|
58
140
|
- 无 PRD → 用用户给的一句话当标题。
|
|
59
|
-
- 建完 push:`
|
|
141
|
+
- 建完 push:`cap.mcp.call("sync", {"direction": "push"})`
|
|
60
142
|
|
|
61
143
|
**rank(RICE)—— 一次取全,别逐个问:**
|
|
62
144
|
1. `task.py list --status planning` 拿全待排任务
|
|
@@ -105,7 +187,7 @@ RICE = Reach × Impact × Confidence ÷ Effort
|
|
|
105
187
|
|
|
106
188
|
`create` / `finish` / `archive` 改了任务状态,都要推给团队(用户永不碰 git):
|
|
107
189
|
```bash
|
|
108
|
-
|
|
190
|
+
cap.mcp.call("sync", {"direction": "push"})
|
|
109
191
|
```
|
|
110
192
|
|
|
111
193
|
## finish 后:自动生成测试交接画像(可选增强)
|
|
@@ -124,7 +206,7 @@ python "$R/.qoder/scripts/team_sync.py" push
|
|
|
124
206
|
|
|
125
207
|
## 🧩 QoderWork 增强:待办双向同步(可选 · 无连接器则自动回退)
|
|
126
208
|
|
|
127
|
-
>
|
|
209
|
+
> 通过 capability 层自动路由(Settings → Connectors,授权 Microsoft 365 待办 / OneNote,或 macOS 提醒事项)。
|
|
128
210
|
> **纯 Qoder IDE / CLI 没有连接器 → 本段不生效,任务只在本地 task.json 管理,完全够用。**
|
|
129
211
|
> 探测方式:仅当运行环境提供了「待办 / To-Do」工具可直接读写时才用。
|
|
130
212
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: wl-test
|
|
3
3
|
description: "测试工作流: 意图式用例 + Playwright MCP 执行 + 锚点缓存。触发: /wl-test 命令; 或自然语言里带'功能验证点'(如'测一下XX支不支持YY''验一下XX能不能ZZ''XX对不对')。纯随手看('打开XX页''看看登录')→不走本工作流。需确认(DANGEROUS)。"
|
|
4
|
-
trigger: "/wl
|
|
4
|
+
trigger: "user invokes /wl-* command explicitly"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# wl-test — 测试工作流(/wl-test 命令 或 带功能验证点的自然语言触发)
|
|
@@ -32,9 +32,9 @@ trigger: "/wl-test 命令; 或自然语言带具体功能验证点: '测一下XX
|
|
|
32
32
|
|
|
33
33
|
**① 必须用意图式格式(intent+anchor),禁止旧 action+selector 格式。**
|
|
34
34
|
```json
|
|
35
|
-
// ✅ 对: 意图式, anchor 来自 recall
|
|
35
|
+
// ✅ 对: 意图式, anchor 来自 recall 或 snapshot
|
|
36
36
|
{"intent":"fill","desc":"账号输入框","value":"{{ask:登录账号}}","anchor":{"role":"textbox","name":"尊敬的管理员"}}
|
|
37
|
-
// ❌ 错: 旧 action+CSS selector (脆弱, recall 锚点白沉淀)
|
|
37
|
+
// ❌ 错: 旧 action+CSS selector (脆弱, 脆弱, recall 锚点白沉淀)
|
|
38
38
|
{"action":"form_input","target":"input[placeholder*='账号']","value":"{{ask:测试账号}}"}
|
|
39
39
|
```
|
|
40
40
|
|
|
@@ -46,20 +46,48 @@ trigger: "/wl-test 命令; 或自然语言带具体功能验证点: '测一下XX
|
|
|
46
46
|
**③ 用例 JSON 必须用 `--cases-file` 传文件,禁止内联 `--cases`。**
|
|
47
47
|
```bash
|
|
48
48
|
# ✅ 对: 写文件再传路径(不截断)
|
|
49
|
-
python "$R/.qoder/scripts/
|
|
49
|
+
python "$R/.qoder/scripts/orchestration/wlkj.py" test quick --cases-file <文件路径> --env test --platform web
|
|
50
50
|
# ❌ 错: 内联(长JSON截断, 多条用例必丢)
|
|
51
|
-
python "$R/.qoder/scripts/
|
|
51
|
+
python "$R/.qoder/scripts/orchestration/wlkj.py" test quick --cases '[{"case_id"...(超长)...}]'
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
**④
|
|
54
|
+
**④ 无锚点时必须先 snapshot 拿真实 anchor 再填,禁止预猜 CSS selector。**
|
|
55
55
|
```bash
|
|
56
|
-
|
|
56
|
+
# recall 空时 → 不预猜! 先 navigate + snapshot 拿到真实 a11y 树
|
|
57
|
+
# 再把 snapshot 里看到的 role+name 填进 step.anchor
|
|
57
58
|
```
|
|
58
|
-
recall
|
|
59
|
+
- recall 命中 → 直接用 recall 的 anchor(确定性命中,零 LLM)
|
|
60
|
+
- recall 空(首次)→ **navigate + browser_snapshot → 从 a11y 树提取 anchor → 填进用例**
|
|
61
|
+
- **绝不**在没看页面的情况下凭"登录页一般有账号框"去猜 selector
|
|
59
62
|
|
|
60
|
-
**⑤
|
|
61
|
-
- `{{
|
|
62
|
-
-
|
|
63
|
+
**⑤ URL 必须写成 `{{base_url}}/auth/login`(带斜杠)。**
|
|
64
|
+
- base_url 已去尾斜杠(脚本行为),写 `{{base_url}}auth/login` 会拼成 `https://xxxauth/login` 坏地址。
|
|
65
|
+
- 脚本会检测并告警,但 AI 应一次写对。
|
|
66
|
+
|
|
67
|
+
**⑥ recall 的 `--url` 必须用相对路径(无前导斜杠),避开 Git-bash/MSYS 路径转换坑。**
|
|
68
|
+
```bash
|
|
69
|
+
# ✅ 对: 相对路径 (MSYS 不转换)
|
|
70
|
+
python "$R/.qoder/scripts/orchestration/wlkj.py" test recall --url auth/login
|
|
71
|
+
# ❌ 错: 前导斜杠会被 Windows Git-bash 转成 /D:/.../Git/auth/login
|
|
72
|
+
python "$R/.qoder/scripts/orchestration/wlkj.py" test recall --url /auth/login
|
|
73
|
+
```
|
|
74
|
+
(脚本已加 normalize 兜底能修这个,但别依赖兜底——直接写相对路径最干净。)
|
|
75
|
+
|
|
76
|
+
**⑦ Playwright 工具一次性批量加载,snapshot 结果直接用内存 a11y 树,禁止写文件再 Read。**
|
|
77
|
+
```bash
|
|
78
|
+
# ✅ 对: 一个回合里并发加载所有要用的 browser_* 工具描述
|
|
79
|
+
# ✅ 对: browser_snapshot 返回的 a11y 树直接在对话里读, 不落地文件
|
|
80
|
+
# ❌ 错: 用一个加载一个(调用数翻倍); snapshot 写 yml 文件 → 再 Read 读回来(每步2次调用)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**⑧ 验证码识别一气呵成(截图→Read→type→删图),不绕路、不预先纠结。**
|
|
84
|
+
- 登录用例里**不写**验证码 step——点登录后若弹验证码,执行循环自动走 5 步。
|
|
85
|
+
- 固定 5 步连发:`browser_snapshot` 找验证码图 → `browser_take_screenshot` 存图 → Read 看图算 → `browser_type` 填 → 删图。
|
|
86
|
+
- 验证码有时效(1-2分钟),所以**填完立刻点登录**,别在中间插别的操作。
|
|
87
|
+
- 自识别连失 2 次才交人。
|
|
88
|
+
|
|
89
|
+
**⑨ 用例必须一次生成全部(正向+异常),禁止一条一条生成。**
|
|
90
|
+
- 每生成一条 = 一次模型调用。一次写完 Q-1(正向) + Q-2(异常) + ... 全部。
|
|
63
91
|
|
|
64
92
|
## ⚙️ 自取上下文(Quest / QoderWork 无 hook 注入,必须自读)
|
|
65
93
|
|
|
@@ -70,8 +98,8 @@ recall 返回的每个锚点,直接用它的 role+name 填进 step.anchor。
|
|
|
70
98
|
> R=$(python ~/.qoderwork/repo_root.py 2>/dev/null) || R=.
|
|
71
99
|
> ```
|
|
72
100
|
> `repo_root.py` 从 `~/.qoderwork/mcp.json` 反推仓库根;失败回退 `.`(IDE/CLI 工作目录即仓库根)。
|
|
73
|
-
>
|
|
74
|
-
> 如果 `repo_root.py` 报错找不到,先在仓库里跑 `python "$R/.qoder/scripts/
|
|
101
|
+
> **后续脚本统一用 `python "$R/.qoder/scripts/orchestration/wlkj.py" <命令>`,不要再搜!**
|
|
102
|
+
> 如果 `repo_root.py` 报错找不到,先在仓库里跑 `python "$R/.qoder/scripts/orchestration/wlkj.py" install-qw`。
|
|
75
103
|
|
|
76
104
|
- `<R>/.qoder/.developer` — 当前开发者
|
|
77
105
|
- `<R>/.qoder/config.yaml` 的 `autotest:` 段 — 环境/域名
|
|
@@ -84,7 +112,7 @@ recall 返回的每个锚点,直接用它的 role+name 填进 step.anchor。
|
|
|
84
112
|
|
|
85
113
|
**唯一正确做法**——1 条命令查 MySQL sys_menu 真实路由:
|
|
86
114
|
```bash
|
|
87
|
-
python "$R/.qoder/scripts/
|
|
115
|
+
python "$R/.qoder/scripts/orchestration/wlkj.py" search --routes 保险
|
|
88
116
|
# → 保险管理 -> /veh/vehicle/vehAffair/insurance
|
|
89
117
|
```
|
|
90
118
|
- 数据来自 MySQL `sys_menu` 表(后台动态菜单,**唯一事实源**)
|
|
@@ -93,10 +121,10 @@ python "$R/.qoder/scripts/search_index.py" --routes 保险
|
|
|
93
121
|
|
|
94
122
|
### 生成用例时用 MySQL MCP 补真实数据(少手填、覆盖边界)
|
|
95
123
|
|
|
96
|
-
1. **取真实测试数据**:需要业务单号/账号时 `
|
|
124
|
+
1. **取真实测试数据**:需要业务单号/账号时 `cap.mcp.call("query_data", {"table": "表名", "columns": "id", "单号列", "where": "条件", "limit": "5"})`
|
|
97
125
|
- 拿测试库真实可用的数据,别编造。填进用例的 data 里。
|
|
98
126
|
- [QAS环境] 数据行仅参考格式,不代表线上。
|
|
99
|
-
2. **枚举边界覆盖**:涉及状态/类型字段时 `
|
|
127
|
+
2. **枚举边界覆盖**:涉及状态/类型字段时 `cap.mcp.call("query_distinct", {"table": "表名", "column": "状态列"})`
|
|
100
128
|
- 查真实取值范围(如 case_status = 1/2/3/4),按每个值造边界用例。
|
|
101
129
|
- 取值范围可信 → 用例枚举全覆盖,不漏分支。
|
|
102
130
|
|
|
@@ -131,30 +159,66 @@ coverage_matrix()
|
|
|
131
159
|
|
|
132
160
|
**不要任务/PRD。零搜索。** 用意图+锚点格式(A方案),observe-act-extract 执行。
|
|
133
161
|
|
|
134
|
-
### Step 0
|
|
162
|
+
### Step 0 + 0.5:recall 查锚点 + context_pack 取全(两者无依赖,同回合并发)
|
|
163
|
+
|
|
164
|
+
> Step0(recall 锚点)和 Step0.5(context_pack 上下文)互不依赖 → **同一条消息并发发**,别先跑0再跑0.5。
|
|
165
|
+
|
|
166
|
+
**Step 0:recall 查锚点**(用相对 URL,避开 MSYS 坑)
|
|
135
167
|
```bash
|
|
136
|
-
python "$R/.qoder/scripts/
|
|
168
|
+
python "$R/.qoder/scripts/orchestration/wlkj.py" test recall --url auth/login
|
|
137
169
|
```
|
|
138
170
|
- **有锚点** → 生成用例时直接把 anchor 填进 step(确定性命中,快、零 LLM)
|
|
139
|
-
-
|
|
171
|
+
- **无锚点(首次)** → **先 navigate + snapshot 拿真实锚点,再生成用例**(见 Step 0.6)
|
|
140
172
|
|
|
141
|
-
|
|
173
|
+
**Step 0.5:用知识图谱 MCP 增强用例**(1 次调用取全,与 recall 并发)
|
|
174
|
+
```python
|
|
175
|
+
context_pack(keyword='功能名', platform='web', role='test')
|
|
176
|
+
```
|
|
177
|
+
- 返回(role=test 裁剪): 代码落点 + 相关历史 PRD 标题 + API + 字段
|
|
178
|
+
- 拿到后用于: API→断言点, 字段→fill 步骤更准
|
|
142
179
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
180
|
+
> ⚠️ **注意:context_pack 不抓 PRD 验收标准正文**(它只取 PRD 的 title+keywords,
|
|
181
|
+
> 不解析「## 验收标准」章节)。**业务规则断言不要指望 context_pack**,它给不了 expected。
|
|
182
|
+
> 要把 PRD 验收标准翻译成可执行断言,用 `assertion-gen` 子命令(见下方专节)。
|
|
146
183
|
|
|
147
184
|
> 图谱无数据 → 跳过,按通用模式生成。**不要再单独调 feature_overview**(信息重叠,浪费一轮)。
|
|
148
185
|
|
|
186
|
+
### Step 0.6:recall 空时,先 snapshot 拿真实锚点(🔴 首次跑关键,禁止预猜 selector)
|
|
187
|
+
|
|
188
|
+
**recall 返回空 = 第一次测这页。此时绝不能凭"登录页一般有账号框"去猜 CSS selector。**
|
|
189
|
+
正确做法:先用 Playwright 打开页面拿一次 a11y 树,从里面提取真实 anchor:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
# 1. 批量加载要用的 browser_* 工具描述 (一次并发, 别一个一个加载)
|
|
193
|
+
# 最少: browser_navigate, browser_snapshot, browser_click, browser_type,
|
|
194
|
+
# browser_take_screenshot, browser_evaluate
|
|
195
|
+
|
|
196
|
+
# 2. browser_navigate 到 {{base_url}}/auth/login (base_url 从 config 取, 已去尾斜杠)
|
|
197
|
+
# 3. browser_snapshot 一次 → 拿到 a11y 树 (直接读对话里的内容, 不写文件)
|
|
198
|
+
```
|
|
199
|
+
snapshot 返回形如:
|
|
200
|
+
```
|
|
201
|
+
- textbox "尊敬的管理员,请输入您的账号" [ref=e36]
|
|
202
|
+
- textbox "请输入您的密码" [ref=e42]
|
|
203
|
+
- textbox "验证码" [ref=e56]
|
|
204
|
+
- button "login" [ref=e64]: 登录
|
|
205
|
+
```
|
|
206
|
+
**把这里的 role+name 直接填进用例的 anchor**:
|
|
207
|
+
```json
|
|
208
|
+
{"intent":"fill","desc":"账号输入框","value":"{{ask:登录账号}}","anchor":{"role":"textbox","name":"尊敬的管理员,请输入您的账号"}}
|
|
209
|
+
```
|
|
210
|
+
> 这一步把"首次跑要靠 LLM 兜底匹配"变成"确定性命中",是省调用的根本。
|
|
211
|
+
> 拿到锚点后继续 Step 1。
|
|
212
|
+
|
|
149
213
|
**🎯 知道页面名/URL 时,AI 先查按钮+接口画像(最精准,1 条命令拿到所有交互点):**
|
|
150
214
|
```bash
|
|
151
|
-
python "$R/.qoder/scripts/
|
|
152
|
-
python "$R/.qoder/scripts/
|
|
215
|
+
python "$R/.qoder/scripts/orchestration/wlkj.py" test page --kw 保险 # 按业务名查
|
|
216
|
+
python "$R/.qoder/scripts/orchestration/wlkj.py" test page --url /veh/insurance # 按URL查
|
|
153
217
|
```
|
|
154
218
|
(这是 AI 在 `/wl-test` 流程内部自动调用的工具,**用户只用 `/wl-test`,不碰此命令**。)
|
|
155
219
|
输出每个按钮的 handler + 调用接口 + HTTP 方法 + 置信度,**直接对应到用例的 click→assert 断言点**。开发 `finish` 任务时也会自动生成 `test-handoff.md`,内容同此画像——交接场景直接读那份即可,不必重查。
|
|
156
220
|
|
|
157
|
-
### Step 1:AI 据用户描述 +
|
|
221
|
+
### Step 1:AI 据用户描述 + 锚点生成意图式用例 JSON(**一次生成全部用例**)
|
|
158
222
|
**意图+锚点格式**(A方案核心):用 `intent` 表达"做什么",用 `anchor` 精确定位(有锚点则确定性命中,无则 LLM 兜底)。
|
|
159
223
|
```json
|
|
160
224
|
[{"case_id":"Q-1","title":"正确账号密码登录","platform":"web","env":"test",
|
|
@@ -181,7 +245,7 @@ python "$R/.qoder/scripts/autotest.py" page --url /veh/insurance # 按URL查
|
|
|
181
245
|
# AI 先写文件:
|
|
182
246
|
# 写 workspace/members/{dev}/drafts/_autotest-cases.json
|
|
183
247
|
# 再调:
|
|
184
|
-
python "$R/.qoder/scripts/
|
|
248
|
+
python "$R/.qoder/scripts/orchestration/wlkj.py" test quick \
|
|
185
249
|
--cases-file workspace/members/{dev}/drafts/_autotest-cases.json \
|
|
186
250
|
--desc "<用户描述>" --platform <web|app>
|
|
187
251
|
```
|
|
@@ -220,32 +284,40 @@ python "$R/.qoder/scripts/autotest.py" quick \
|
|
|
220
284
|
> ⚠️ **导航是 `browser_navigate`(不是 BrowserNavigate / navigate)**。
|
|
221
285
|
> 调用前对照上面清单,名字写错 = 工具不存在 = 整个测试失败。
|
|
222
286
|
|
|
287
|
+
**🔴 调用效率(实测:一次登录测试不该超过 12 步调用):**
|
|
288
|
+
1. **工具批量加载**:进入执行阶段时,在一个回合里并发加载所有要用的 `browser_*` 工具描述(navigate/snapshot/click/type/take_screenshot/evaluate),不要用一个加载一个。
|
|
289
|
+
2. **snapshot 不落地**:`browser_snapshot` 返回的 a11y 树**直接在对话里读**,禁止写 yml 文件再 Read 回来(每步省一次调用)。
|
|
290
|
+
3. **有 anchor 连发不思考**:锚点已确定,`browser_type`/`browser_click` 直接按 role+name 操作,零模型调用。
|
|
291
|
+
|
|
223
292
|
**对每个 step(用 Playwright MCP 工具,连着发,别每步深度思考):**
|
|
224
293
|
1. **intent=goto** → `browser_navigate`(1 次)
|
|
225
|
-
2. **首次到新页面** → `browser_snapshot` **1 次**拿 a11y
|
|
294
|
+
2. **首次到新页面** → `browser_snapshot` **1 次**拿 a11y 树(直接读,不写文件)
|
|
226
295
|
3. **intent=click/fill**(锚点优先,**确定性匹配不思考**):
|
|
227
296
|
- **有 anchor** → snapshot 结果里按 `role+name`/`placeholder` 直接匹配 → 拿到元素 → `browser_type`/`browser_click`(**零模型调用,连着发**)
|
|
228
297
|
- **无 anchor** → 用 `desc` 语义匹配(1 次模型调用)
|
|
229
298
|
- 匹配失败 → self-heal:重新 `browser_snapshot` + 重试,最多 2 次
|
|
230
|
-
4. **intent=extract** → 看 URL /
|
|
299
|
+
4. **intent=extract** → 看 URL / 当前页面判断(1 次模型调用)
|
|
231
300
|
5. **intent=ask_human** → 暂停等人
|
|
232
301
|
6. **intent=assert** → 确定性校验(零模型调用)
|
|
233
302
|
|
|
234
|
-
### Step 3.5
|
|
303
|
+
### Step 3.5:验证码识别一条龙(数学题)— 一气呵成,别绕路
|
|
235
304
|
|
|
236
305
|
> **🔴 验证码铁律(实测反复踩坑):**
|
|
237
306
|
> - **禁止用 Computer 截图 / zoom 截验证码**——实测反复失败、超时、浪费 5-6 步。
|
|
238
307
|
> - **禁止填默认值/瞎蒙**(如 000000)。
|
|
239
308
|
> - **唯一正确路径**:JS 提取 base64 → 存图 → Read 看 → 算答案 → 填 → 删图。固定 5 步,不偏离。
|
|
309
|
+
> - **验证码有时效(1-2 分钟),所以填完立刻点登录,中间别插别的操作。**
|
|
240
310
|
|
|
241
|
-
按这个**固定顺序**一次走完(用 Playwright MCP 工具,约 30
|
|
242
|
-
1. **`browser_snapshot
|
|
311
|
+
按这个**固定顺序**一次走完(用 Playwright MCP 工具,约 30 秒,**连发不思考**):
|
|
312
|
+
1. **`browser_snapshot`**(如果刚才已经 snapshot 过且页面没变,可跳过)看页面元素,找到验证码图片元素
|
|
243
313
|
2. **`browser_take_screenshot`** 截验证码图片 → 存临时文件(稳定,不像 Computer 截图会失败)
|
|
244
314
|
3. **Read 工具看图 → 算出答案**(QoderWork 自带视觉,1 次模型调用,数学题识别率高)
|
|
245
315
|
4. **`browser_type`** 填答案到验证码输入框
|
|
246
|
-
5.
|
|
247
|
-
|
|
316
|
+
5. **立刻 `browser_click` 点登录**(验证码刚填、还没过期,抓住窗口)
|
|
317
|
+
6. **删掉临时图片**(bash `del _captcha.png`)
|
|
318
|
+
> 验证码过期了(提示"验证码已失效")→ 别纠结,直接刷新重走 1-5 步。
|
|
248
319
|
> 识别完**必须删图**,不留垃圾。
|
|
320
|
+
> 自识别连续失败 2 次或明显是短信/扫码类 → 才交人(见 Step A 铁律⑤)。
|
|
249
321
|
|
|
250
322
|
### Step 4:回收结果 + 自学习沉淀(🔴 必须,不做完不出报告)
|
|
251
323
|
|
|
@@ -254,7 +326,7 @@ python "$R/.qoder/scripts/autotest.py" quick \
|
|
|
254
326
|
|
|
255
327
|
**执行方式**:把测试结果 + 测试中 `browser_snapshot` 看到的页面元素一起回传:
|
|
256
328
|
```bash
|
|
257
|
-
python "$R/.qoder/scripts/
|
|
329
|
+
python "$R/.qoder/scripts/orchestration/wlkj.py" test quick \
|
|
258
330
|
--cases-file <之前的cases文件> \
|
|
259
331
|
--record '{"Q-1":"pass","Q-2":"pass","Q-3":"pass"}' \
|
|
260
332
|
--page-json '{"url":"/veh/vehicle/vehAffair/insurance","elements":[{"role":"textbox","name":"车牌号"},{"role":"button","name":"搜索"},{"role":"button","name":"新增"}]}'
|
|
@@ -275,11 +347,11 @@ python "$R/.qoder/scripts/autotest.py" quick \
|
|
|
275
347
|
## browser 基于任务的浏览器测试(用户明确给了任务名时)
|
|
276
348
|
|
|
277
349
|
```bash
|
|
278
|
-
|
|
279
|
-
python "$R/.qoder/scripts/
|
|
350
|
+
cap.mcp.call("list_tasks", {}) # 确认任务存在
|
|
351
|
+
python "$R/.qoder/scripts/orchestration/wlkj.py" test generate <task> # 生成骨架到 autotest-cases.jsonl
|
|
280
352
|
# AI 读任务 PRD「验收标准」补全 steps/expected (真实数据用 {{ask:}}, 不搜源码)
|
|
281
|
-
python "$R/.qoder/scripts/
|
|
282
|
-
python "$R/.qoder/scripts/
|
|
353
|
+
python "$R/.qoder/scripts/orchestration/wlkj.py" test run <task> # 注入+出执行计划
|
|
354
|
+
python "$R/.qoder/scripts/orchestration/wlkj.py" test run <task> --record '{"..":"pass"}' # 回收结果
|
|
283
355
|
```
|
|
284
356
|
|
|
285
357
|
**回归测试增强(图谱 MCP)**:测接口改动时,用 `get_impact` 查影响范围,自动覆盖受影响页面:
|
|
@@ -300,6 +372,43 @@ JUnit 测试(Mock + MockMvc,Given-When-Then,`@DisplayName`,AssertJ)→
|
|
|
300
372
|
|
|
301
373
|
---
|
|
302
374
|
|
|
375
|
+
## 📋 断言生成 assertion-gen(业务规则断言 · 第三档专用)
|
|
376
|
+
|
|
377
|
+
> 解决痛点:quick 模式的 `_check_expect` 能执行 `{fetch, jsonpath, equals}` 断言,
|
|
378
|
+
> 但那个 **equals 期望值从哪来** 没人管——靠 LLM 瞎编不可信。
|
|
379
|
+
> `assertion-gen` 把"PRD 验收标准 → 可执行断言"这条断链接上。
|
|
380
|
+
|
|
381
|
+
**分工(呼应图谱边界三条红线):**
|
|
382
|
+
- **人补语义**:PRD 里写结构化验收(Given-When-Then,或"条件-操作-预期"带接口名/状态码/字段值)
|
|
383
|
+
- **图谱补字段**:`page_probe` 查按钮→接口,`field_map` 查中文标题→字段名
|
|
384
|
+
- **真跑定对错**:assertion-gen 只产断言 JSON,跑不跑交给 quick/webaccess
|
|
385
|
+
|
|
386
|
+
**什么时候用**:业务规则断言(保险延期要审批、退保校验、状态流转)——这是 quick 通用模式
|
|
387
|
+
搞不定的第三档。冒烟(页面没崩)、CRUD 流(新增→列表出现)用 quick 就够,不需要这个。
|
|
388
|
+
|
|
389
|
+
```bash
|
|
390
|
+
# 从任务 PRD 的验收标准章节抿断言
|
|
391
|
+
python "$R/.qoder/scripts/orchestration/wlkj.py" test assertion-gen --task <任务名> --page 保险
|
|
392
|
+
|
|
393
|
+
# 临时用 GWT 文本兜底(PRD 没写验收标准时)
|
|
394
|
+
python "$R/.qoder/scripts/orchestration/wlkj.py" test assertion-gen \
|
|
395
|
+
--gwt "Given 已登录保险页 When 点击延期提交 Then 接口 /api/insurance/delay 返回 code=200 且 状态列显示审批中" \
|
|
396
|
+
--page 保险
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
**输出**:`workspace/tasks/<task>/assertions.json`,每条断言带 `source` 置信度标记:
|
|
400
|
+
- `规则·句中含接口` / `图谱·按钮画像` = 可信(接口路径已确定)
|
|
401
|
+
- `需确认·字段未命中` = 待补(字段表查不到,留 `{{ask:}}` 占位)
|
|
402
|
+
|
|
403
|
+
**两条铁律(assertion-gen 内置):**
|
|
404
|
+
1. **抽不到就老实说**:PRD 验收标准为空/纯描述 → 输出"无可抽取的结构化断言",**绝不编造**断言。
|
|
405
|
+
2. **语义不背书**:字段补全命中标 `[图谱]`,查不到留占位标 `[需确认]`,不让图谱假装懂业务。
|
|
406
|
+
|
|
407
|
+
**下一步**:把 `assertions.json` 的 assertions 数组并进 quick 用例的 steps(`intent:"assert"`),
|
|
408
|
+
用 webaccess 或 quick 执行真跑验证。**assertion-gen 自己不执行、不真跑。**
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
303
412
|
## 🔐 测试数据处理原则(个人 vs 团队隔离)
|
|
304
413
|
|
|
305
414
|
- **绝不编造**:账号/密码/手机号用 `{{ask:描述}}` 占位,问用户要。
|
|
@@ -307,9 +416,9 @@ JUnit 测试(Mock + MockMvc,Given-When-Then,`@DisplayName`,AssertJ)→
|
|
|
307
416
|
域名才放 config(全团队一致)。账号密码每人不同 → 个人文档。
|
|
308
417
|
- **个人文档**:`workspace/members/{你的名字}/autotest-data.yaml`(gitignored,永不 push)
|
|
309
418
|
```bash
|
|
310
|
-
python "$R/.qoder/scripts/
|
|
311
|
-
python "$R/.qoder/scripts/
|
|
312
|
-
python "$R/.qoder/scripts/
|
|
419
|
+
python "$R/.qoder/scripts/orchestration/wlkj.py" test init-data # 生成模板
|
|
420
|
+
python "$R/.qoder/scripts/orchestration/wlkj.py" test set-data 登录账号 test --env test # 填(按环境/平台分块)
|
|
421
|
+
python "$R/.qoder/scripts/orchestration/wlkj.py" test list-data --env test # 查看(密码脱敏)
|
|
313
422
|
```
|
|
314
423
|
run/quick 时按 env/platform 自动取最精确的值;`--data` 可临时覆盖。
|
|
315
424
|
|
|
@@ -319,7 +428,7 @@ run/quick 时按 env/platform 自动取最精确的值;`--data` 可临时覆
|
|
|
319
428
|
|
|
320
429
|
**Step A:查登录态**
|
|
321
430
|
```bash
|
|
322
|
-
python "$R/.qoder/scripts/
|
|
431
|
+
python "$R/.qoder/scripts/orchestration/wlkj.py" test login-state show
|
|
323
432
|
```
|
|
324
433
|
|
|
325
434
|
**Step B:按登录态决定行为**
|
|
@@ -340,8 +449,8 @@ python "$R/.qoder/scripts/autotest.py" login-state show
|
|
|
340
449
|
|
|
341
450
|
```bash
|
|
342
451
|
# 手动操作(一般不用,AI 会自动):
|
|
343
|
-
python "$R/.qoder/scripts/
|
|
344
|
-
python "$R/.qoder/scripts/
|
|
452
|
+
python "$R/.qoder/scripts/orchestration/wlkj.py" test login-state mark --env pre --platform web # 标记已登录
|
|
453
|
+
python "$R/.qoder/scripts/orchestration/wlkj.py" test login-state clear --env pre # 清除(强制重登录)
|
|
345
454
|
```
|
|
346
455
|
|
|
347
456
|
## 🧩 QoderWork 增强:浏览器自动执行(可选 · 无连接器则自动回退)
|
|
@@ -76,6 +76,13 @@
|
|
|
76
76
|
#### 原型地址
|
|
77
77
|
{prototype-{feature}.html 的路径;两端需求分别给 -web/-app 两个文件}
|
|
78
78
|
|
|
79
|
+
> **原型可选(v3.1)**:若本需求为纯规则/接口/定时/参数下发/计算类(无 UI 变化),
|
|
80
|
+
> 经用户在「轻反思+批量确认」环节明确同意不画原型后,本节写:
|
|
81
|
+
> `无({原因},经用户确认豁免)`
|
|
82
|
+
> 并在 PRD 第 1 行 `@contract` 头标记 `prototype: none`。
|
|
83
|
+
> 下游 `/wl-design` 读到 `prototype: none` 跳过原型环节;`/wl-code` 直接以 PRD 字段表为准。
|
|
84
|
+
> eval_prd 检测到 `prototype: none` 时 A2 自动跳过(满分从 100 降到 70,照样能 PASS)。
|
|
85
|
+
|
|
79
86
|
## 非功能性需求
|
|
80
87
|
{性能(响应时间/并发)、安全(权限)、兼容性(浏览器/系统版本)、可用性}
|
|
81
88
|
|