@hupan56/wlkj 2.5.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/bin/cli.js +289 -12
  2. package/package.json +1 -1
  3. package/templates/qoder/agents/insight-planning.md +67 -0
  4. package/templates/qoder/agents/insight-research.md +61 -0
  5. package/templates/qoder/agents/prd-quick.md +1 -0
  6. package/templates/qoder/agents/prd-reference.md +10 -2
  7. package/templates/qoder/commands/optional/wl-insight.md +275 -0
  8. package/templates/qoder/commands/{wl-report.md → optional/wl-report.md} +13 -5
  9. package/templates/qoder/commands/{wl-spec.md → optional/wl-spec.md} +1 -1
  10. package/templates/qoder/commands/{wl-status.md → optional/wl-status.md} +28 -2
  11. package/templates/qoder/commands/wl-code.md +10 -2
  12. package/templates/qoder/commands/wl-commit.md +1 -1
  13. package/templates/qoder/commands/wl-design-draw.md +78 -0
  14. package/templates/qoder/commands/wl-design-scan.md +108 -0
  15. package/templates/qoder/commands/wl-design-spec.md +154 -0
  16. package/templates/qoder/commands/wl-design.md +32 -0
  17. package/templates/qoder/commands/wl-init.md +24 -3
  18. package/templates/qoder/commands/wl-prd-full.md +226 -0
  19. package/templates/qoder/commands/wl-prd-quick.md +134 -0
  20. package/templates/qoder/commands/wl-prd-review.md +104 -0
  21. package/templates/qoder/commands/wl-prd.md +17 -288
  22. package/templates/qoder/commands/wl-search.md +66 -30
  23. package/templates/qoder/commands/wl-task.md +290 -59
  24. package/templates/qoder/commands/wl-test.md +92 -24
  25. package/templates/qoder/config.yaml +59 -15
  26. package/templates/qoder/hooks/inject-workflow-state.py +35 -9
  27. package/templates/qoder/hooks/session-start.py +144 -62
  28. package/templates/qoder/rules/wl-pipeline.md +216 -105
  29. package/templates/qoder/scripts/__pycache__/search_index.cpython-39.pyc +0 -0
  30. package/templates/qoder/scripts/archive_prd.py +377 -0
  31. package/templates/qoder/scripts/autotest.py +1715 -0
  32. package/templates/qoder/scripts/autotest_batch.py +224 -0
  33. package/templates/qoder/scripts/autotest_run.py +297 -0
  34. package/templates/qoder/scripts/benchmark.py +210 -209
  35. package/templates/qoder/scripts/build_style_index.py +444 -4
  36. package/templates/qoder/scripts/check_carriers.py +238 -0
  37. package/templates/qoder/scripts/check_mcp.py +298 -0
  38. package/templates/qoder/scripts/check_qoderwork_consistency.py +166 -0
  39. package/templates/qoder/scripts/common/developer.py +26 -19
  40. package/templates/qoder/scripts/common/events.py +46 -0
  41. package/templates/qoder/scripts/common/extract.py +419 -0
  42. package/templates/qoder/scripts/common/graph_traverse.py +533 -0
  43. package/templates/qoder/scripts/common/identity.py +6 -1
  44. package/templates/qoder/scripts/common/paths.py +89 -0
  45. package/templates/qoder/scripts/common/pip_install.py +144 -0
  46. package/templates/qoder/scripts/common/platform_guard.py +61 -0
  47. package/templates/qoder/scripts/common/search_engine.py +205 -205
  48. package/templates/qoder/scripts/common/terms.py +57 -0
  49. package/templates/qoder/scripts/common/ts_extract.py +536 -0
  50. package/templates/qoder/scripts/context_pack.py +73 -13
  51. package/templates/qoder/scripts/enrich_prompt.py +226 -0
  52. package/templates/qoder/scripts/eval_prd.py +318 -225
  53. package/templates/qoder/scripts/export.py +487 -487
  54. package/templates/qoder/scripts/extract_api_params.py +246 -0
  55. package/templates/qoder/scripts/extract_routes.py +54 -0
  56. package/templates/qoder/scripts/extract_routes_tree.py +78 -0
  57. package/templates/qoder/scripts/fill_prototype.py +707 -0
  58. package/templates/qoder/scripts/gen_design_doc.py +394 -0
  59. package/templates/qoder/scripts/git_sync.py +27 -15
  60. package/templates/qoder/scripts/init_doctor.py +306 -41
  61. package/templates/qoder/scripts/install_qoderwork.py +366 -9
  62. package/templates/qoder/scripts/kg.py +708 -0
  63. package/templates/qoder/scripts/kg_auto_login.py +196 -0
  64. package/templates/qoder/scripts/kg_build.py +612 -0
  65. package/templates/qoder/scripts/kg_build_db.py +327 -0
  66. package/templates/qoder/scripts/kg_duckdb.py +549 -0
  67. package/templates/qoder/scripts/kg_incremental.py +393 -0
  68. package/templates/qoder/scripts/kg_link_db.py +224 -0
  69. package/templates/qoder/scripts/kg_mcp_server.py +801 -0
  70. package/templates/qoder/scripts/kg_semantic.py +150 -0
  71. package/templates/qoder/scripts/kg_test_runner.py +241 -0
  72. package/templates/qoder/scripts/lanhu_stdio_wrapper.py +119 -0
  73. package/templates/qoder/scripts/learn.py +118 -39
  74. package/templates/qoder/scripts/learn_aggregate.py +201 -0
  75. package/templates/qoder/scripts/mcp_launcher.py +359 -0
  76. package/templates/qoder/scripts/mysql_mcp_server.py +396 -0
  77. package/templates/qoder/scripts/repo_root.py +106 -0
  78. package/templates/qoder/scripts/role.py +12 -0
  79. package/templates/qoder/scripts/run_weekly_update.bat +5 -0
  80. package/templates/qoder/scripts/run_weekly_update.sh +5 -0
  81. package/templates/qoder/scripts/search_index.py +307 -60
  82. package/templates/qoder/scripts/secure-ls.js +5640 -0
  83. package/templates/qoder/scripts/setup.py +706 -453
  84. package/templates/qoder/scripts/setup_lanhu.py +963 -0
  85. package/templates/qoder/scripts/status.py +250 -11
  86. package/templates/qoder/scripts/sync_carriers.py +259 -0
  87. package/templates/qoder/scripts/syncgate.py +5 -4
  88. package/templates/qoder/scripts/task.py +75 -0
  89. package/templates/qoder/scripts/team_sync.py +60 -4
  90. package/templates/qoder/scripts/workspace_init.py +1 -1
  91. package/templates/qoder/skills/design-import/SKILL.md +226 -0
  92. package/templates/qoder/skills/design-import/figma-workflow.md +81 -0
  93. package/templates/qoder/skills/design-review/SKILL.md +82 -25
  94. package/templates/qoder/skills/prd-generator/SKILL.md +185 -58
  95. package/templates/qoder/skills/prd-review/SKILL.md +18 -1
  96. package/templates/qoder/skills/prompt-enrich/SKILL.md +90 -0
  97. package/templates/qoder/skills/prototype-generator/SKILL.md +256 -141
  98. package/templates/qoder/skills/prototype-generator/SKILL.md.zcode-79180-2af4721f-f9a6-412c-88db-c0af680d211b.tmp +0 -0
  99. package/templates/qoder/skills/spec-coder/SKILL.md +18 -1
  100. package/templates/qoder/skills/spec-generator/SKILL.md +18 -1
  101. package/templates/qoder/skills/test-generator/SKILL.md +15 -2
  102. package/templates/qoder/skills/wl-code/SKILL.md +55 -36
  103. package/templates/qoder/skills/wl-commit/SKILL.md +89 -76
  104. package/templates/qoder/skills/wl-design/SKILL.md +55 -0
  105. package/templates/qoder/skills/wl-init/SKILL.md +76 -67
  106. package/templates/qoder/skills/wl-insight/SKILL.md +201 -81
  107. package/templates/qoder/skills/wl-prd-full/SKILL.md +69 -0
  108. package/templates/qoder/skills/wl-prd-quick/SKILL.md +49 -0
  109. package/templates/qoder/skills/wl-prd-review/SKILL.md +34 -0
  110. package/templates/qoder/skills/wl-report/SKILL.md +131 -107
  111. package/templates/qoder/skills/wl-search/SKILL.md +141 -75
  112. package/templates/qoder/skills/wl-spec/SKILL.md +49 -39
  113. package/templates/qoder/skills/wl-status/SKILL.md +83 -61
  114. package/templates/qoder/skills/wl-task/SKILL.md +132 -58
  115. package/templates/qoder/skills/wl-test/SKILL.md +406 -40
  116. package/templates/qoder/templates/prd-full-template.md +2 -0
  117. package/templates/qoder/templates/prd-quick-template.md +1 -0
  118. package/templates/qoder/templates/prototype-app.html +13 -8
  119. package/templates/qoder/templates/prototype-web.html +376 -93
  120. package/templates/root/AGENTS.md +89 -34
  121. package/templates/root/requirements.txt +21 -0
  122. package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +259 -259
  123. package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +186 -186
  124. package/templates/qoder/agents/prd-planning.md +0 -56
  125. package/templates/qoder/agents/prd-research.md +0 -33
  126. package/templates/qoder/commands/wl-insight.md +0 -51
  127. package/templates/qoder/skills/wl-prd/SKILL.md +0 -89
@@ -1,40 +1,406 @@
1
- ---
2
- name: wl-test
3
- description: "为已实现代码生成单元测试(/wl-test 命令入口)。Generate unit tests for implemented code. 用户说'写单元测试''补测试''覆盖一下''写个test'或输入 /wl-test 时触发。需用户确认(DANGEROUS)。"
4
- trigger: "代码实现完成;用户说'写测试''补单测''生成test''/wl-test'"
5
- ---
6
-
7
- # wl-test — 生成单元测试(/wl-test 命令入口)
8
-
9
- > 这是 `/wl-test` 命令的 skill 入口。让 QoderWork 的 `/` 列表也能用 `/wl-test`。
10
- > ⚠️ DANGEROUS 操作(写测试文件),执行前向用户确认。
11
- > 完整规则见功能型 skill `.qoder/skills/test-generator/SKILL.md`
12
- > 和命令 `.qoder/commands/wl-test.md`。
13
-
14
- ## ⚙️ 自取上下文(Quest / QoderWork 无 hook 注入,必须自读)
15
-
16
- - `.qoder/.developer` — 当前开发者
17
- - `.qoder/.current-task` — 当前活动任务(找刚实现的代码)
18
- - 活动 task 的实现代码 + 对应 spec
19
-
20
- ## 执行
21
-
22
- 1. **找目标代码**:
23
- ```bash
24
- python .qoder/scripts/search_index.py <类名或关键词>
25
- ```
26
- 没参数就用当前 task 的实现代码。
27
- 2. **读代码 + spec**:理解要测什么
28
- 3. **按 test-generator skill 生成**,覆盖:
29
- - 每个函数的 happy path
30
- - 边界值(null/空/边界)
31
- - 错误处理
32
- - 集成点
33
- 4. **自检**:
34
- - [ ] 测试能跑
35
- - [ ] 覆盖主场景
36
- - [ ] 断言正确
37
- - [ ] 遵循团队测试约定
38
- 5. **报告**:告诉用户生成了哪些测试,建议下一步 `/wl-commit`
39
-
40
- 详细测试规范见 `.qoder/skills/test-generator/SKILL.md`。
1
+ ---
2
+ name: wl-test
3
+ description: "测试工作流: 意图式用例 + Playwright MCP 执行 + 锚点缓存。触发: /wl-test 命令; 或自然语言里带'功能验证点'(如'测一下XX支不支持YY''验一下XX能不能ZZ''XX对不对')。纯随手看('打开XX页''看看登录')→不走本工作流。需确认(DANGEROUS)。"
4
+ trigger: "/wl-test 命令; 或自然语言带具体功能验证点: '测一下XX支不支持YY''验一下XX能不能''XX功能对不对''回归XX'。仅'打开/看看/瞄一眼'等无验证点的不触发。"
5
+ ---
6
+
7
+ # wl-test — 测试工作流(/wl-test 命令 或 带功能验证点的自然语言触发)
8
+
9
+ > ⚠️ **触发边界(关键:区分"功能测试" vs "随手看")**:
10
+ > - **走本工作流**:用户带**具体功能验证点**——"测一下异常记录**支不支持**项目类型搜索""验一下导出**能不能**用""保险搜索**对不对**"。
11
+ > (含"支不支持/能不能/对不对/是否/有没有/测一下XX的YY功能"= 要验证一个行为点 = 功能测试)
12
+ > - ❌ **不走本工作流**:纯随手操作——"打开保险页""看看登录""帮我点一下新增"。(无验证点,QoderWork 用自带浏览器直接做即可)
13
+ > 区别:本工作流产出**可复用的测试用例 + 锚点沉淀 + 执行报告**;随手操作不沉淀。
14
+
15
+ 三条线,按用户意图路由。**默认 quick**(最常用、最快)。
16
+
17
+ ## 🎯 核心原则(最重要,务必遵守)
18
+
19
+ 1. **🔴 用户只说测什么,AI 全自动跑完,全程不问"要不要我跑"。**
20
+ 禁止让用户写 `--env test --platform web` 这种参数(平台/环境 AI 自己定,web 默认、test 默认)。
21
+ **禁止问"要我跑还是你来跑""我帮你测还是你自己测""要不要现在执行"——/wl-test 触发即代表要跑,直接跑到出报告。**
22
+ 不要问地址、不要问登录方式——config/个人文档里都有。
23
+ **唯一允许打断用户的场景**:填验证码这种真人校验(见铁律⑤),其它一律自动做完。
24
+ 2. **🚫 quick 模式绝不搜代码、绝不读源码、绝不查 API。** 用户说"测登录",AI 直接据
25
+ 该类页面的通用交互模式生成用例,不跑 search_index、不读 .vue。搜代码是 unit 的事。
26
+ 3. **🚫 脚本不自己开浏览器。** autotest.py 只生成用例+注入数据+出执行计划。真正的
27
+ 浏览器操作由 **QoderWork Browser Use 连接器**承担。AI 绝不在脚本里 import playwright/selenium。
28
+ 4. **已登录就别重复登录。** 先查登录态。已登录 用例直接 goto 目标功能页。未登录 → 才生成登录步骤。
29
+ 5. **缺啥问啥、一次问清。** 能合并的合并,别一个参数问一轮。
30
+
31
+ ## 🔴 quick 执行铁律(违反任何一条 = 测试失败,实测踩过的坑)
32
+
33
+ **① 必须用意图式格式(intent+anchor),禁止旧 action+selector 格式。**
34
+ ```json
35
+ // 对: 意图式, anchor 来自 recall
36
+ {"intent":"fill","desc":"账号输入框","value":"{{ask:登录账号}}","anchor":{"role":"textbox","name":"尊敬的管理员"}}
37
+ // 错: 旧 action+CSS selector (脆弱, recall 锚点白沉淀)
38
+ {"action":"form_input","target":"input[placeholder*='账号']","value":"{{ask:测试账号}}"}
39
+ ```
40
+
41
+ **② 占位符必须用 `{{ask:登录账号}}` / `{{ask:登录密码}}`(精确匹配个人文档的 key)。**
42
+ - 个人文档 `autotest-data.yaml` 里存的是 `登录账号` / `登录密码`。
43
+ - 用 `{{ask:测试账号}}` / `{{ask:测试密码}}` → 读不到 → 问用户 → **违反原则①**。
44
+ - 脚本 `quick` 会自动注入,**不要问用户账号密码**。
45
+
46
+ **③ 用例 JSON 必须用 `--cases-file` 传文件,禁止内联 `--cases`。**
47
+ ```bash
48
+ # ✅ 对: 写文件再传路径(不截断)
49
+ python "$R/.qoder/scripts/autotest.py" quick --cases-file <文件路径> --env test --platform web
50
+ # ❌ 错: 内联(长JSON截断, 多条用例必丢)
51
+ python "$R/.qoder/scripts/autotest.py" quick --cases '[{"case_id"...(超长)...}]'
52
+ ```
53
+
54
+ **④ 执行前必须 recall 查锚点。有锚点 → 直接填进 anchor,不调 LLM 匹配。**
55
+ ```bash
56
+ python "$R/.qoder/scripts/autotest.py" recall --url "/auth/login"
57
+ ```
58
+ recall 返回的每个锚点,直接用它的 role+name 填进 step.anchor。这步是**省模型调用**的根本。
59
+
60
+ **⑤ 验证码必须用 Playwright MCP 的 `browser_take_screenshot` 截图 → QoderWork 自带视觉识别 → `browser_type` 填入 → 删图。禁止用 Computer 截图(实测反复失败超时)、禁止瞎蒙默认值。**
61
+ - `{{ask:}}` 占位符里**绝不出现验证码**——验证码不存个人文档,执行时实时识别。
62
+ - 截图工具不可用时 → 用 JS 提取 base64 → 存图 → Read 看 → 算答案 → 填入 → **删图**。
63
+
64
+ ## ⚙️ 自取上下文(Quest / QoderWork 无 hook 注入,必须自读)
65
+
66
+ > 🔴 **脚本路径——不要反复 dir 搜索!**
67
+ >
68
+ > **先确定仓库根 R**(QoderWork 桌面端工作目录不是仓库根,相对路径会失效):
69
+ > ```bash
70
+ > R=$(python ~/.qoderwork/repo_root.py 2>/dev/null) || R=.
71
+ > ```
72
+ > `repo_root.py` 从 `~/.qoderwork/mcp.json` 反推仓库根;失败回退 `.`(IDE/CLI 工作目录即仓库根)。
73
+ > **读到 1 次记住,后续所有脚本统一用 `python "$R/.qoder/scripts/xxx.py"`,不要再搜!**
74
+ > 如果 `repo_root.py` 报错找不到,先在仓库里跑 `python "$R/.qoder/scripts/install_qoderwork.py"`。
75
+
76
+ - `<R>/.qoder/.developer` — 当前开发者
77
+ - `<R>/.qoder/config.yaml` 的 `autotest:` 段 — 环境/域名
78
+ - **quick 不需要**读任务/PRD/代码。browser 才需要任务目录。
79
+
80
+ ### 🔴 测试页面 URL 用 --routes 查,禁止 grep 路由文件!(实测 QoderWork 反复踩坑)
81
+
82
+ **🚫 绝对禁止**:grep 源码里的 `router/routes/*.ts` 找 URL、读 `core.ts` 搜 component、从 API 端点反推 URL。
83
+ (实测:QoderWork 里 AI 反复 grep `abnormalManage.*index.vue in core.ts` 搜了十几遍无匹配,纯浪费。)
84
+
85
+ **唯一正确做法**——1 条命令查 MySQL sys_menu 真实路由:
86
+ ```bash
87
+ python "$R/.qoder/scripts/search_index.py" --routes 保险
88
+ # → 保险管理 -> /veh/vehicle/vehAffair/insurance
89
+ ```
90
+ - 数据来自 MySQL `sys_menu` 表(后台动态菜单,**唯一事实源**)
91
+ - 没找到 → 先 `--routes` 搜英文关键词(insurance/asset/attendance),再试中文
92
+ - **还是没找到 → 直接问用户页面叫什么,不要去 grep 源码**
93
+
94
+ ### 生成用例时用 MySQL MCP 补真实数据(少手填、覆盖边界)
95
+
96
+ 1. **取真实测试数据**:需要业务单号/账号时 `mcp__qoder-mysql__query_data(table='表名', columns='id,单号列', where='条件', limit=5)`
97
+ - 拿测试库真实可用的数据,别编造。填进用例的 data 里。
98
+ - [QAS环境] 数据行仅参考格式,不代表线上。
99
+ 2. **枚举边界覆盖**:涉及状态/类型字段时 `mcp__qoder-mysql__query_distinct(table='表名', column='状态列')`
100
+ - 查真实取值范围(如 case_status = 1/2/3/4),按每个值造边界用例。
101
+ - 取值范围可信 → 用例枚举全覆盖,不漏分支。
102
+
103
+ ## 🚦 路由(仅在 /wl-test 命令或明确测试任务触发时,决定走哪条线)
104
+
105
+ | 用户输入 | 路由 | 要不要搜代码 |
106
+ |---------|------|-------------|
107
+ | `/wl-test`(不带参数)/ "测试盲区" | **coverage** — 调 `coverage_matrix` 展示哪些功能没测 | ❌ |
108
+ | `/wl-test 测一下登录` / "跑登录测试用例" | **quick**(默认) | ❌ |
109
+ | `/wl-test browser 06-14-login` / "回归测试 XXX 任务" | **browser** | ❌(读任务PRD) |
110
+ | `/wl-test unit` / "写单元测试" | **unit** | ✅(这条才搜代码) |
111
+
112
+ > ⚠️ **不触发本工作流的情况**(用 QoderWork 自带浏览器即可):
113
+ > "测一下这个页面""看看登录能不能打开""帮我打开浏览器操作一下"——这些是随手操作,不走 autotest.py、不沉淀锚点、不产出测试报告。
114
+
115
+ 没明说任务名、只是随口"测一下 XX" → **quick**。
116
+
117
+ ---
118
+
119
+ ## coverage 测试覆盖视图(`/wl-test` 不带参数时)
120
+
121
+ 用户只输入 `/wl-test` 或说"测试盲区"→ 调知识图谱的 `coverage_matrix`,展示哪些功能有测试、哪些是空白:
122
+ ```
123
+ coverage_matrix()
124
+ → 资产管理[✓有测试] / 考勤[✗无测试] / 薪资[✗无测试] / 保险[✓有测试]
125
+ ```
126
+ 然后建议:"考勤和薪资是盲区,要补测吗?" 用户选一个 → 进入 quick/browser 生成用例。
127
+
128
+ ---
129
+
130
+ ## quick 快速测试(默认 · 详)
131
+
132
+ **不要任务/PRD。零搜索。** 用意图+锚点格式(A方案),observe-act-extract 执行。
133
+
134
+ ### Step 0:先 recall 查有没有现成锚点(跳过 LLM)
135
+ ```bash
136
+ python "$R/.qoder/scripts/autotest.py" recall --url "/auth/login"
137
+ ```
138
+ - **有锚点** → 生成用例时直接把 anchor 填进 step(确定性命中,快、零 LLM)
139
+ - **无锚点** → 用意图式生成(首次会调 LLM 匹配,执行后 learn 沉淀,下次就快)
140
+
141
+ ### Step 0.5:用知识图谱 MCP 增强用例(1 次调用取全)
142
+
143
+ **只调 1 次** `context_pack(keyword='功能名', platform='web', role='test')`:
144
+ - 返回(role=test 裁剪): 代码 + PRD验收标准 + API + 数据库表结构(第8段)
145
+ - 拿到后用于: PRD验收标准→用例expected, API→断言点, 字段→fill步骤更准
146
+
147
+ > 图谱无数据 → 跳过,按通用模式生成。**不要再单独调 feature_overview**(信息重叠,浪费一轮)。
148
+
149
+ ### Step 1:AI 据用户描述 + 图谱信息生成意图式用例 JSON(**一次生成全部用例**)
150
+ **意图+锚点格式**(A方案核心):用 `intent` 表达"做什么",用 `anchor` 精确定位(有锚点则确定性命中,无则 LLM 兜底)。
151
+ ```json
152
+ [{"case_id":"Q-1","title":"正确账号密码登录","platform":"web","env":"test",
153
+ "steps":[
154
+ {"intent":"goto","desc":"登录页","value":"{{base_url}}/auth/login"},
155
+ {"intent":"fill","desc":"账号输入框","value":"{{ask:登录账号}}",
156
+ "anchor":{"role":"textbox","name":"尊敬的管理员,请输入我的账号"}},
157
+ {"intent":"fill","desc":"密码输入框","value":"{{ask:登录密码}}",
158
+ "anchor":{"role":"textbox","name":"请输入您的密码"}},
159
+ {"intent":"click","desc":"登录按钮","anchor":{"role":"button","name":"登录"}},
160
+ {"intent":"extract","desc":"是否已离开登录页进入系统"}
161
+ ],
162
+ "expected":"登录成功进系统"}]
163
+ ```
164
+ > 验证码不是单独的 step——登录按钮点击后,如果跳出验证码弹层/数学题,执行循环会**自动**走铁律⑤的 5 步自识别(screenshot→Read→type→删图),不需要在用例里写 `ask_human`。只有自识别连失 2 次才暂停交人。
165
+ **intent 取值**:`goto`(导航) / `observe`(读页面) / `click`(点击) / `fill`(填表) / `extract`(语义断言) / `ask_human`(人机协同) / `assert`(确定性断言)
166
+ **anchor 取值**(稳定锚点,不随刷新变):`{role, name, placeholder, text}` 任一组合。recall 有就用,没有就留空(执行时 LLM 兜底匹配)。
167
+ 真实数据用 `{{ask:描述}}` 占位;URL 用 `{{base_url}}`。
168
+ > ⚠️ **一次生成所有用例**(正向+异常),不要一条一条生成——每生成一条 = 一次模型调用,耗不起。
169
+
170
+ ### Step 2:用文件传 cases(根治命令行截断)+ 注入数据 + 出执行计划
171
+ **把用例 JSON 写文件,再传路径**(不要内联 --cases,长 JSON 会截断):
172
+ ```bash
173
+ # AI 先写文件:
174
+ # 写 workspace/members/{dev}/drafts/_autotest-cases.json
175
+ # 再调:
176
+ python "$R/.qoder/scripts/autotest.py" quick \
177
+ --cases-file workspace/members/{dev}/drafts/_autotest-cases.json \
178
+ --desc "<用户描述>" --platform <web|app>
179
+ ```
180
+ 脚本自动:解析环境域名 + 从个人文档注入账号密码 + 出执行计划。
181
+ 若提示"需要测试数据 N 项" → 逐项问用户,再用 `--data` 传。
182
+
183
+ ### Step 3:执行循环(observe-act-extract)
184
+
185
+ > **🔴 必须用 Playwright MCP 工具驱动浏览器,禁止用 QoderWork 自带连接器!**
186
+ >
187
+ > 自带连接器(tabs_context/navigate/computer)实测**极慢**(5-8 分钟)、tab 管理混乱、
188
+ > Computer 截图反复失败。**Playwright MCP 快 30 倍、稳定、不折腾 tab。**
189
+ >
190
+ > | 操作 | 用 Playwright MCP 的工具 | ❌ 禁止用自带连接器的 |
191
+ > |------|------------------------|---------------------|
192
+ > | 导航 | `browser_navigate` | tabs_context_mcp / Navigate |
193
+ > | 读页面 | `browser_snapshot` | read_page / Find |
194
+ > | 填表单 | `browser_type` | form_input |
195
+ > | 点击 | `browser_click` | Computer click |
196
+ > | 截图 | `browser_take_screenshot` | Computer screenshot/zoom |
197
+ > | 执行JS | `browser_evaluate` | javascript_tool |
198
+ >
199
+ > **如果你看不到 browser_* 开头的工具,说明 Playwright MCP 没生效——检查 Settings → MCP 里
200
+ > playwright 是否启用,或重启 QoderWork。**
201
+
202
+ > **🔴 工具名必须逐字匹配,禁止自创/大小写错(实测 QoderWork 里 AI 把 `browser_navigate` 调成 `BrowserNavigate` 导致全失败)。**
203
+ > Playwright MCP(`@playwright/mcp`)注册的**全部 23 个工具**(snake_case 小写,逐字照抄):
204
+ > ```
205
+ > browser_navigate browser_navigate_back browser_tabs browser_close browser_resize
206
+ > browser_snapshot browser_click browser_type browser_fill_form browser_select_option
207
+ > browser_hover browser_drag browser_drop browser_press_key browser_file_upload
208
+ > browser_take_screenshot browser_wait_for browser_evaluate browser_handle_dialog
209
+ > browser_console_messages browser_network_requests browser_network_request browser_run_code_unsafe
210
+ > ```
211
+ > ⚠️ **截图工具是 `browser_take_screenshot`(不是 browser_screenshot)**。
212
+ > ⚠️ **导航是 `browser_navigate`(不是 BrowserNavigate / navigate)**。
213
+ > 调用前对照上面清单,名字写错 = 工具不存在 = 整个测试失败。
214
+
215
+ **对每个 step(用 Playwright MCP 工具,连着发,别每步深度思考):**
216
+ 1. **intent=goto** → `browser_navigate`(1 次)
217
+ 2. **首次到新页面** → `browser_snapshot` **1 次**拿 a11y 树
218
+ 3. **intent=click/fill**(锚点优先,**确定性匹配不思考**):
219
+ - **有 anchor** → snapshot 结果里按 `role+name`/`placeholder` 直接匹配 → 拿到元素 → `browser_type`/`browser_click`(**零模型调用,连着发**)
220
+ - **无 anchor** → 用 `desc` 语义匹配(1 次模型调用)
221
+ - 匹配失败 → self-heal:重新 `browser_snapshot` + 重试,最多 2 次
222
+ 4. **intent=extract** → 看 URL / read_page 判断(1 次模型调用)
223
+ 5. **intent=ask_human** → 暂停等人
224
+ 6. **intent=assert** → 确定性校验(零模型调用)
225
+
226
+ ### Step 3.5:验证码识别一条龙(数学题)
227
+
228
+ > **🔴 验证码铁律(实测反复踩坑):**
229
+ > - **禁止用 Computer 截图 / zoom 截验证码**——实测反复失败、超时、浪费 5-6 步。
230
+ > - **禁止填默认值/瞎蒙**(如 000000)。
231
+ > - **唯一正确路径**:JS 提取 base64 → 存图 → Read 看 → 算答案 → 填 → 删图。固定 5 步,不偏离。
232
+
233
+ 按这个**固定顺序**一次走完(用 Playwright MCP 工具,约 30 秒):
234
+ 1. **`browser_snapshot`** 看页面元素,找到验证码图片元素
235
+ 2. **`browser_take_screenshot`** 截验证码图片 → 存临时文件(稳定,不像 Computer 截图会失败)
236
+ 3. **Read 工具看图 → 算出答案**(QoderWork 自带视觉,1 次模型调用,数学题识别率高)
237
+ 4. **`browser_type`** 填答案到验证码输入框
238
+ 5. **删掉临时图片**(bash `del _captcha.png`)
239
+ > 验证码有时效(1-2 分钟),所以要快。过期了刷新重走一遍。
240
+ > 识别完**必须删图**,不留垃圾。
241
+
242
+ ### Step 4:回收结果 + 自学习沉淀(🔴 必须,不做完不出报告)
243
+
244
+ > **测试跑完 ≠ 结束。** 出报告前必须回收结果 + 沉淀锚点,让下次测同页更快(Stagehand 式缓存)。
245
+ > **不做这步 = 白测**(下次还得从零找元素)。
246
+
247
+ **执行方式**:把测试结果 + 测试中 `browser_snapshot` 看到的页面元素一起回传:
248
+ ```bash
249
+ python "$R/.qoder/scripts/autotest.py" quick \
250
+ --cases-file <之前的cases文件> \
251
+ --record '{"Q-1":"pass","Q-2":"pass","Q-3":"pass"}' \
252
+ --page-json '{"url":"/veh/vehicle/vehAffair/insurance","elements":[{"role":"textbox","name":"车牌号"},{"role":"button","name":"搜索"},{"role":"button","name":"新增"}]}'
253
+ ```
254
+
255
+ `--record` 回收时自动:
256
+ - ① 从 `--page-json` 提取页面元素锚点 → 存进 `test-pages.json`
257
+ - ② 从**通过的** intent 用例提取 goto URL + anchor → 沉淀
258
+ - ③ 登录用例通过 → 自动标记已登录
259
+
260
+ **🔴 `--page-json` 的 elements 怎么来的**:测试中每次 `browser_snapshot` 看到的元素,记下来,回传时填进去。
261
+ 不需要全部——**只填这个页面关键的交互元素**(搜索框/按钮/Tab/表格),5-10 个就够。
262
+
263
+ **沉淀效果**:下次 `/wl-test 测保险` → recall 命中保险页锚点 → 不用 snapshot 找元素 → 直接操作 → **省一半时间**。
264
+
265
+ ---
266
+
267
+ ## browser 基于任务的浏览器测试(用户明确给了任务名时)
268
+
269
+ ```bash
270
+ python "$R/.qoder/scripts/task.py" list # 确认任务存在
271
+ python "$R/.qoder/scripts/autotest.py" generate <task> # 生成骨架到 autotest-cases.jsonl
272
+ # AI 读任务 PRD「验收标准」补全 steps/expected (真实数据用 {{ask:}}, 不搜源码)
273
+ python "$R/.qoder/scripts/autotest.py" run <task> # 注入+出执行计划
274
+ python "$R/.qoder/scripts/autotest.py" run <task> --record '{"..":"pass"}' # 回收结果
275
+ ```
276
+
277
+ **回归测试增强(图谱 MCP)**:测接口改动时,用 `get_impact` 查影响范围,自动覆盖受影响页面:
278
+ ```
279
+ get_impact(endpoint='/asset/list')
280
+ → 返回: 资产列表页、资产导出按钮、资产详情页都调这个接口
281
+ → 为每个受影响页面生成回归用例
282
+ ```
283
+
284
+ ---
285
+
286
+ ## unit 单元测试(说"单元测试/单测"才走这条)
287
+
288
+ 读 **test-generator** skill:用 `search_index.py` 定位实现代码 → 读 spec → 生成
289
+ JUnit 测试(Mock + MockMvc,Given-When-Then,`@DisplayName`,AssertJ)→
290
+ 输出 `tests/{package}/{Class}Test.java`。
291
+ **(只有这条线才搜代码、读源码。)**
292
+
293
+ ---
294
+
295
+ ## 🔐 测试数据处理原则(个人 vs 团队隔离)
296
+
297
+ - **绝不编造**:账号/密码/手机号用 `{{ask:描述}}` 占位,问用户要。
298
+ - **个人测试数据放个人文档,绝不进 config.yaml**:config 是团队共用会 push,放账号=泄漏。
299
+ 域名才放 config(全团队一致)。账号密码每人不同 → 个人文档。
300
+ - **个人文档**:`workspace/members/{你的名字}/autotest-data.yaml`(gitignored,永不 push)
301
+ ```bash
302
+ python "$R/.qoder/scripts/autotest.py" init-data # 生成模板
303
+ python "$R/.qoder/scripts/autotest.py" set-data 登录账号 test --env test # 填(按环境/平台分块)
304
+ python "$R/.qoder/scripts/autotest.py" list-data --env test # 查看(密码脱敏)
305
+ ```
306
+ run/quick 时按 env/platform 自动取最精确的值;`--data` 可临时覆盖。
307
+
308
+ ## 🔁 登录态 + 登录前置(内部页面测试的前提)
309
+
310
+ **测考勤/资产/保险等内部页面,前提是已登录。** 处理逻辑(一句话:能复用就复用,不能就 AI 自己登,验证码按铁律⑤走):
311
+
312
+ **Step A:查登录态**
313
+ ```bash
314
+ python "$R/.qoder/scripts/autotest.py" login-state show
315
+ ```
316
+
317
+ **Step B:按登录态决定行为**
318
+ - **已登录** → 用例直接 `goto` 目标功能页(如 `/attendance`),**不带任何登录步骤**
319
+ - **未登录** → **AI 自己生成登录用例并自动跑完**(账号密码从个人文档注入,验证码按铁律⑤ 的 5 步流程:`browser_take_screenshot` 截图 → Read 看图算答案 → `browser_type` 填入 → 删图)。
320
+ - **只有在**截图识别连续失败 2 次、或验证码明显是短信/扫码这类 AI 必定过不了的形式时,才退回人机协同:暂停一次,让用户手动过验证码,用户说"好了"就继续。**不要一上来就甩给用户。**
321
+
322
+ > 🔴 **不要再说"AI 过不了验证码"这种话**——铁律⑤ 已经给了能跑通的自识别路径(Playwright 截图 + QoderWork/VLM 视觉)。默认先自己试,不行再交人。先放弃 = 违反原则①。
323
+
324
+ **Step C:执行时双重校验登录态**
325
+ 即使 login-state 显示已登录,执行时也要**实际验证**(goto 目标页后看有没有被踢回登录页):
326
+ - `browser_navigate` 到考勤页 → `browser_snapshot` 看当前页面
327
+ - 如果 URL 变成 `/auth/login` 或页面是登录表单 → **session 过期了** → 自动生成登录用例重登(不要让用户手动登)
328
+ - 如果正常显示考勤页面 → 继续执行用例
329
+
330
+ **执行计划里**:若 login-state 显示已登录,显示 "💡 已登录 ... 直接 goto 目标页即可"。
331
+ 若发现 session 过期/被踢 → 自动重登,不打断用户。
332
+
333
+ ```bash
334
+ # 手动操作(一般不用,AI 会自动):
335
+ python "$R/.qoder/scripts/autotest.py" login-state mark --env pre --platform web # 标记已登录
336
+ python "$R/.qoder/scripts/autotest.py" login-state clear --env pre # 清除(强制重登录)
337
+ ```
338
+
339
+ ## 🧩 QoderWork 增强:浏览器自动执行(可选 · 无连接器则自动回退)
340
+
341
+ > 依赖 QoderWork 桌面端的 Browser Use / Computer 控制连接器(Settings → Connectors)。
342
+ > **纯 Qoder IDE/CLI 无连接器 → 降级为人工核对清单,不报错。**
343
+ - 有连接器:Agent 读脚本出的执行计划,用 Browser Use 驱动 Chrome 真实操作 → 记 pass/fail → 回传
344
+ - 无连接器:输出人工核对清单,用户自己在浏览器点
345
+
346
+ ### 🔐 验证码 / 登录页(默认自识别,兜底人机协同)
347
+
348
+ > ⚠️ **本工作流的默认策略是 AI 自识别验证码**(见 quick 铁律⑤ + Step 3.5),不是"全部甩给用户"。
349
+ > 下面这段是**兜底**:自识别跑不通时(连续失败 2 次 / 短信扫码类验证码)才退到人机协同。
350
+
351
+ 登录页带**数学题验证码**时,用 `solve_captcha` action 声明:
352
+ ```json
353
+ {"action":"solve_captcha",
354
+ "target":"<验证码图片元素>",
355
+ "fill_target":"<答案输入框>",
356
+ "desc":"验证码: 先 AI 自识别(screenshot→Read算→type), 失败2次再交人"}
357
+ ```
358
+
359
+ **执行到 solve_captcha 时,AI 这样走(先自己试,不行才交人):**
360
+ 1. **先自己识别**:按 Step 3.5 的 5 步——`browser_take_screenshot` 截验证码 → Read 看图算出答案 → `browser_type` 填入 → 删图(**这是默认路径,先走这条**)
361
+ 2. **连续 2 次识别失败 / 验证码明显是短信/扫码** → 才退人机协同:告诉用户"账号密码已填好,验证码我没识别出来,请你手动完成并点登录,好了告诉我"
362
+ 3. **等待**用户确认登录成功(AI 可 `browser_snapshot` 看是否已离开登录页判断)
363
+ 4. 用户登录成功后,AI 继续执行后续用例
364
+
365
+ **不要一开始就跳到第 2 步**——那等于放弃了自识别能力,违背本工作流全自动的目标。
366
+
367
+ ### 🔁 登录态复用(能复用就别重复登录)
368
+
369
+ **已登录的浏览器共享 cookie/session。** 所以:
370
+ - 生成用例前,先 `browser_snapshot` 看当前页面,判断是否已登录(不在登录页 = 已登录)
371
+ - **已登录** → 用例直接 `goto` 目标功能页,不写登录步骤(最快)
372
+ - **未登录** → AI 自己生成登录用例跑完(验证码按上面策略:先自识别,兜底交人)
373
+
374
+ > 不存在"必须用户先手动登录"的硬性要求——AI 能自己登就自己登。
375
+
376
+ ### 🛠️ 两种连接器 + 工具冲突降级(实战经验)
377
+
378
+ QoderWork 有**两种**浏览器控制方式,遇到问题要在两者间切换:
379
+
380
+ **① Browser Use(builtin_browser,Chrome 扩展)**——精度高、直接操作 DOM
381
+ - ✅ 能用:`navigate` / `read_page` / `find`(只读工具,几乎不挂)
382
+ - ❌ 易挂:`computer`(点击/截图) / `form_input`(表单) / `javascript_tool`(JS注入) —— 被 Chrome 扩展冲突拦
383
+ - 挂的原因:其他扩展(Claude/Codex/其他 debugger 扩展)抢占了 debugger 通道
384
+
385
+ **② Computer Use(builtin_computer_use,桌面自动化)**——系统级,不依赖 Chrome 扩展
386
+ - ✅ 不受扩展冲突影响(它是模拟鼠标键盘,不走 debugger)
387
+ - ⚠️ 要求:**浏览器窗口必须在最前台**(前台是别的应用如 ToDesk/IDE 时会操作到错误窗口)
388
+ - ⚠️ 靠坐标,精度比 Browser Use 低
389
+
390
+ **降级决策(遇到工具报错时按此走,别直接 BLOCKED):**
391
+
392
+ | 场景 | 处理 |
393
+ |------|------|
394
+ | Browser Use 的 form_input/click 挂了 | ① 先让用户关冲突扩展重试;② 不行就**切 Computer Use** + 提示用户**把浏览器切到前台** |
395
+ | Computer Use 操作了错误窗口 | 提示用户:浏览器切前台(别让 ToDesk/IDE 挡着),再重试 |
396
+ | read_page 能用但点击不能 | 用 read_page 拿到元素的坐标/ref,Computer Use 按坐标点 |
397
+
398
+ **`chrome-extension://` 冲突**:`chrome://extensions` 禁用其他自动化扩展(Claude/Codex),
399
+ 只留 QoderWork 连接器。若一时解不了,**切 Computer Use + 浏览器前台**是可靠的绕过路径。
400
+
401
+ ## 输出规则
402
+ - **触发即跑,跑完出报告**:/wl-test 触发 = 要测,直接生成用例→执行→出结果,不要问"要我跑吗"
403
+ - quick/browser:用例表 + 执行结果(pass/fail),失败列原因 + 建议,全过建议继续
404
+ - 别在 quick 里啰嗦搜代码的过程——用户要的是"测了没、通没通"
405
+ - 验证码/登录:默认 AI 自识别(截图→算→填),连续 2 次失败或短信/扫码类才交人
406
+ - 工具冲突:先 Playwright MCP,挂了再试 QoderWork Browser Use,最后 Computer Use + 浏览器前台,别直接 BLOCKED
@@ -7,6 +7,8 @@
7
7
 
8
8
  ---
9
9
 
10
+ <!-- mode: reference -->
11
+ <!-- 生成时按实际模式替换上面的值: reference / brainstorm / planning -->
10
12
  # REQ-{YYYY}-{NNN} {需求标题}
11
13
 
12
14
  ## 前言
@@ -6,6 +6,7 @@
6
6
 
7
7
  ---
8
8
 
9
+ <!-- mode: quick -->
9
10
  # {功能模块} - {改动摘要}
10
11
 
11
12
  > 标题让读者一眼知道改了什么,避免"XX功能修改"这类模糊表述。
@@ -4,9 +4,11 @@
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
6
  <title>{{TITLE}} - APP 移动端</title>
7
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vant@4/lib/index.css">
7
8
  <style>
8
9
  :root {
9
- /* Vant Design Tokens */
10
+ /* Vant Design Tokens. {{APP_PRIMARY}} 占位主色 (默认 #1989fa = Vant 标准,
11
+ 与 Carmg-H5 一致)。fill_prototype 可注入项目真实主色。 */
10
12
  --van-primary-color: #1989fa;
11
13
  --van-success-color: #07c160;
12
14
  --van-warning-color: #ff976a;
@@ -21,6 +23,7 @@
21
23
  /* 适配基准: 375px 设计稿 */
22
24
  --app-max-width: 375px;
23
25
  --app-padding: 16px;
26
+ /* design-import spec tokens anchor (fill_prototype 在此注入蓝湖/设计师 spec 的真实 token) */
24
27
  }
25
28
 
26
29
  * { margin: 0; padding: 0; box-sizing: border-box; }
@@ -326,19 +329,21 @@
326
329
 
327
330
  <div class="fab">+</div>
328
331
 
332
+ <!-- 底部 TabBar: {{TAB_BAR_ITEMS}} 填真实模块 (当前页 active)。
333
+ 用 Vant 字体图标 (van-icon-*), 绝不用 emoji。示例 3-4 个 tab。 -->
329
334
  <div class="tab-bar">
335
+ {{TAB_BAR_ITEMS}}
336
+ <!-- 示例 (替换为你的真实底部导航, 当前页加 active):
330
337
  <div class="tab-item active">
331
- <span class="tab-icon">🏠</span>
332
- <span>首页</span>
338
+ <i class="van-icon van-icon-wap-home-o tab-icon"></i><span>首页</span>
333
339
  </div>
334
340
  <div class="tab-item">
335
- <span class="tab-icon">📋</span>
336
- <span>任务</span>
341
+ <i class="van-icon van-icon-todo-list-o tab-icon"></i><span>任务</span>
337
342
  </div>
338
343
  <div class="tab-item">
339
- <span class="tab-icon">👤</span>
340
- <span>我的</span>
344
+ <i class="van-icon van-icon-user-o tab-icon"></i><span>我的</span>
341
345
  </div>
346
+ -->
342
347
  </div>
343
348
  </body>
344
- </html>
349
+ </html>