speccore 8.3.349 → 8.3.363

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 (248) hide show
  1. package/.agents/skills/spec-verify/SKILL.md +22 -1
  2. package/dist/cli.js +15 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/about.d.ts.map +1 -1
  5. package/dist/commands/about.js +36 -1
  6. package/dist/commands/about.js.map +1 -1
  7. package/dist/commands/analyze/add-platform.d.ts +3 -0
  8. package/dist/commands/analyze/add-platform.d.ts.map +1 -0
  9. package/dist/commands/analyze/add-platform.js +82 -0
  10. package/dist/commands/analyze/add-platform.js.map +1 -0
  11. package/dist/commands/analyze/audit-fix.d.ts +3 -0
  12. package/dist/commands/analyze/audit-fix.d.ts.map +1 -0
  13. package/dist/commands/analyze/audit-fix.js +73 -0
  14. package/dist/commands/analyze/audit-fix.js.map +1 -0
  15. package/dist/commands/analyze/context-guard.d.ts +3 -0
  16. package/dist/commands/analyze/context-guard.d.ts.map +1 -0
  17. package/dist/commands/analyze/context-guard.js +75 -0
  18. package/dist/commands/analyze/context-guard.js.map +1 -0
  19. package/dist/commands/analyze/doc.d.ts +3 -0
  20. package/dist/commands/analyze/doc.d.ts.map +1 -0
  21. package/dist/commands/analyze/doc.js +77 -0
  22. package/dist/commands/analyze/doc.js.map +1 -0
  23. package/dist/commands/analyze/feature.d.ts +3 -0
  24. package/dist/commands/analyze/feature.d.ts.map +1 -0
  25. package/dist/commands/analyze/feature.js +75 -0
  26. package/dist/commands/analyze/feature.js.map +1 -0
  27. package/dist/commands/analyze/from-code.d.ts +3 -0
  28. package/dist/commands/analyze/from-code.d.ts.map +1 -0
  29. package/dist/commands/analyze/from-code.js +94 -0
  30. package/dist/commands/analyze/from-code.js.map +1 -0
  31. package/dist/commands/analyze/incremental.d.ts +3 -0
  32. package/dist/commands/analyze/incremental.d.ts.map +1 -0
  33. package/dist/commands/analyze/incremental.js +90 -0
  34. package/dist/commands/analyze/incremental.js.map +1 -0
  35. package/dist/commands/analyze/modes.d.ts +13 -0
  36. package/dist/commands/analyze/modes.d.ts.map +1 -0
  37. package/dist/commands/analyze/modes.js +147 -0
  38. package/dist/commands/analyze/modes.js.map +1 -0
  39. package/dist/commands/analyze/module.d.ts +3 -0
  40. package/dist/commands/analyze/module.d.ts.map +1 -0
  41. package/dist/commands/analyze/module.js +95 -0
  42. package/dist/commands/analyze/module.js.map +1 -0
  43. package/dist/commands/analyze/overview.d.ts +3 -0
  44. package/dist/commands/analyze/overview.d.ts.map +1 -0
  45. package/dist/commands/analyze/overview.js +125 -0
  46. package/dist/commands/analyze/overview.js.map +1 -0
  47. package/dist/commands/analyze/shared.d.ts +53 -0
  48. package/dist/commands/analyze/shared.d.ts.map +1 -0
  49. package/dist/commands/analyze/shared.js +34 -0
  50. package/dist/commands/analyze/shared.js.map +1 -0
  51. package/dist/commands/analyze/units.d.ts +46 -0
  52. package/dist/commands/analyze/units.d.ts.map +1 -0
  53. package/dist/commands/analyze/units.js +950 -0
  54. package/dist/commands/analyze/units.js.map +1 -0
  55. package/dist/commands/analyze.d.ts +6 -88
  56. package/dist/commands/analyze.d.ts.map +1 -1
  57. package/dist/commands/analyze.js +97 -1417
  58. package/dist/commands/analyze.js.map +1 -1
  59. package/dist/commands/ask.d.ts.map +1 -1
  60. package/dist/commands/ask.js +4 -2
  61. package/dist/commands/ask.js.map +1 -1
  62. package/dist/commands/dashboard.d.ts.map +1 -1
  63. package/dist/commands/dashboard.js +36 -1
  64. package/dist/commands/dashboard.js.map +1 -1
  65. package/dist/commands/deploy.d.ts +6 -0
  66. package/dist/commands/deploy.d.ts.map +1 -1
  67. package/dist/commands/deploy.js +153 -0
  68. package/dist/commands/deploy.js.map +1 -1
  69. package/dist/commands/doc2spec.d.ts +1 -1
  70. package/dist/commands/doc2spec.d.ts.map +1 -1
  71. package/dist/commands/doc2spec.js +33 -23
  72. package/dist/commands/doc2spec.js.map +1 -1
  73. package/dist/commands/doctor.js +60 -13
  74. package/dist/commands/doctor.js.map +1 -1
  75. package/dist/commands/execute.d.ts.map +1 -1
  76. package/dist/commands/execute.js +22 -0
  77. package/dist/commands/execute.js.map +1 -1
  78. package/dist/commands/help.js +36 -1
  79. package/dist/commands/help.js.map +1 -1
  80. package/dist/commands/init.js +1 -1
  81. package/dist/commands/init.js.map +1 -1
  82. package/dist/commands/pipeline.d.ts.map +1 -1
  83. package/dist/commands/pipeline.js +15 -0
  84. package/dist/commands/pipeline.js.map +1 -1
  85. package/dist/commands/plan.js +2 -0
  86. package/dist/commands/plan.js.map +1 -1
  87. package/dist/commands/pr.d.ts.map +1 -1
  88. package/dist/commands/pr.js +14 -3
  89. package/dist/commands/pr.js.map +1 -1
  90. package/dist/commands/prompts.d.ts +11 -1
  91. package/dist/commands/prompts.d.ts.map +1 -1
  92. package/dist/commands/prompts.js +197 -15
  93. package/dist/commands/prompts.js.map +1 -1
  94. package/dist/commands/reindex.d.ts.map +1 -1
  95. package/dist/commands/reindex.js +23 -0
  96. package/dist/commands/reindex.js.map +1 -1
  97. package/dist/commands/retro.d.ts.map +1 -1
  98. package/dist/commands/retro.js +3 -1
  99. package/dist/commands/retro.js.map +1 -1
  100. package/dist/commands/status-panel.d.ts.map +1 -1
  101. package/dist/commands/status-panel.js +9 -1
  102. package/dist/commands/status-panel.js.map +1 -1
  103. package/dist/commands/unitize.d.ts +2 -0
  104. package/dist/commands/unitize.d.ts.map +1 -1
  105. package/dist/commands/unitize.js +168 -6
  106. package/dist/commands/unitize.js.map +1 -1
  107. package/dist/commands/update-env-configs.d.ts.map +1 -1
  108. package/dist/commands/update-env-configs.js +10 -0
  109. package/dist/commands/update-env-configs.js.map +1 -1
  110. package/dist/commands/verify.d.ts +8 -0
  111. package/dist/commands/verify.d.ts.map +1 -1
  112. package/dist/commands/verify.js +383 -16
  113. package/dist/commands/verify.js.map +1 -1
  114. package/dist/commands/welcome.d.ts.map +1 -1
  115. package/dist/commands/welcome.js +3 -1
  116. package/dist/commands/welcome.js.map +1 -1
  117. package/dist/core/ai-verify/plan-generator.d.ts +94 -0
  118. package/dist/core/ai-verify/plan-generator.d.ts.map +1 -0
  119. package/dist/core/ai-verify/plan-generator.js +598 -0
  120. package/dist/core/ai-verify/plan-generator.js.map +1 -0
  121. package/dist/core/ai-verify/result-parser.d.ts +27 -0
  122. package/dist/core/ai-verify/result-parser.d.ts.map +1 -0
  123. package/dist/core/ai-verify/result-parser.js +212 -0
  124. package/dist/core/ai-verify/result-parser.js.map +1 -0
  125. package/dist/core/ai-verify/scope-resolver.d.ts +34 -0
  126. package/dist/core/ai-verify/scope-resolver.d.ts.map +1 -0
  127. package/dist/core/ai-verify/scope-resolver.js +183 -0
  128. package/dist/core/ai-verify/scope-resolver.js.map +1 -0
  129. package/dist/core/ai-verify/types.d.ts +171 -0
  130. package/dist/core/ai-verify/types.d.ts.map +1 -0
  131. package/dist/core/ai-verify/types.js +9 -0
  132. package/dist/core/ai-verify/types.js.map +1 -0
  133. package/dist/core/ai-verify/visual-judge.d.ts +33 -0
  134. package/dist/core/ai-verify/visual-judge.d.ts.map +1 -0
  135. package/dist/core/ai-verify/visual-judge.js +123 -0
  136. package/dist/core/ai-verify/visual-judge.js.map +1 -0
  137. package/dist/core/ask-engine.d.ts +6 -0
  138. package/dist/core/ask-engine.d.ts.map +1 -1
  139. package/dist/core/ask-engine.js +83 -3
  140. package/dist/core/ask-engine.js.map +1 -1
  141. package/dist/core/code-graph/builder.d.ts.map +1 -1
  142. package/dist/core/code-graph/builder.js +93 -75
  143. package/dist/core/code-graph/builder.js.map +1 -1
  144. package/dist/core/code-graph/incremental-build.d.ts.map +1 -1
  145. package/dist/core/code-graph/incremental-build.js +13 -19
  146. package/dist/core/code-graph/incremental-build.js.map +1 -1
  147. package/dist/core/code-graph/index.d.ts +1 -1
  148. package/dist/core/code-graph/index.d.ts.map +1 -1
  149. package/dist/core/code-graph/index.js +15 -12
  150. package/dist/core/code-graph/index.js.map +1 -1
  151. package/dist/core/code-graph/multimodal.js +1 -1
  152. package/dist/core/code-graph/parser.js +1 -1
  153. package/dist/core/code-graph/query.d.ts +1 -1
  154. package/dist/core/code-graph/reporter.d.ts +1 -1
  155. package/dist/core/code-graph/reporter.d.ts.map +1 -1
  156. package/dist/core/code-graph/reporter.js +18 -10
  157. package/dist/core/code-graph/reporter.js.map +1 -1
  158. package/dist/core/code-graph/requirement-linker.d.ts.map +1 -1
  159. package/dist/core/code-graph/requirement-linker.js +24 -14
  160. package/dist/core/code-graph/requirement-linker.js.map +1 -1
  161. package/dist/core/code-graph/types.d.ts +1 -1
  162. package/dist/core/code-graph/types.js +1 -1
  163. package/dist/core/code-graph/visualizer.d.ts +1 -7
  164. package/dist/core/code-graph/visualizer.d.ts.map +1 -1
  165. package/dist/core/code-graph/visualizer.js +476 -49
  166. package/dist/core/code-graph/visualizer.js.map +1 -1
  167. package/dist/core/deploy/engine.d.ts +21 -0
  168. package/dist/core/deploy/engine.d.ts.map +1 -1
  169. package/dist/core/deploy/engine.js +276 -0
  170. package/dist/core/deploy/engine.js.map +1 -1
  171. package/dist/core/deploy-ledger.d.ts +12 -0
  172. package/dist/core/deploy-ledger.d.ts.map +1 -0
  173. package/dist/core/deploy-ledger.js +35 -0
  174. package/dist/core/deploy-ledger.js.map +1 -0
  175. package/dist/core/environment-config.d.ts +11 -0
  176. package/dist/core/environment-config.d.ts.map +1 -1
  177. package/dist/core/environment-config.js +4 -0
  178. package/dist/core/environment-config.js.map +1 -1
  179. package/dist/core/intent-cache.d.ts.map +1 -1
  180. package/dist/core/intent-cache.js +6 -0
  181. package/dist/core/intent-cache.js.map +1 -1
  182. package/dist/core/knowledge-visualizer.js +1 -1
  183. package/dist/core/page-bundle.d.ts +2 -0
  184. package/dist/core/page-bundle.d.ts.map +1 -1
  185. package/dist/core/page-bundle.js +18 -1
  186. package/dist/core/page-bundle.js.map +1 -1
  187. package/dist/core/plan-html.js +1 -1
  188. package/dist/core/platform-inference.d.ts +35 -0
  189. package/dist/core/platform-inference.d.ts.map +1 -1
  190. package/dist/core/platform-inference.js +92 -0
  191. package/dist/core/platform-inference.js.map +1 -1
  192. package/dist/core/prompt-plugins/analyze-graph-plugin.d.ts.map +1 -1
  193. package/dist/core/prompt-plugins/analyze-graph-plugin.js +16 -1
  194. package/dist/core/prompt-plugins/analyze-graph-plugin.js.map +1 -1
  195. package/dist/core/prompt-plugins/execute-graph-plugin.d.ts +2 -0
  196. package/dist/core/prompt-plugins/execute-graph-plugin.d.ts.map +1 -1
  197. package/dist/core/prompt-plugins/execute-graph-plugin.js +127 -27
  198. package/dist/core/prompt-plugins/execute-graph-plugin.js.map +1 -1
  199. package/dist/core/quality-audit.d.ts.map +1 -1
  200. package/dist/core/quality-audit.js +18 -0
  201. package/dist/core/quality-audit.js.map +1 -1
  202. package/dist/core/reindex-engine.d.ts +20 -0
  203. package/dist/core/reindex-engine.d.ts.map +1 -1
  204. package/dist/core/reindex-engine.js +142 -0
  205. package/dist/core/reindex-engine.js.map +1 -1
  206. package/dist/core/requirement-clarifier.d.ts.map +1 -1
  207. package/dist/core/requirement-clarifier.js +22 -6
  208. package/dist/core/requirement-clarifier.js.map +1 -1
  209. package/dist/core/spec-paths.d.ts +5 -0
  210. package/dist/core/spec-paths.d.ts.map +1 -1
  211. package/dist/core/spec-paths.js +1 -0
  212. package/dist/core/spec-paths.js.map +1 -1
  213. package/dist/core/test-config.d.ts +7 -1
  214. package/dist/core/test-config.d.ts.map +1 -1
  215. package/dist/core/test-config.js +59 -11
  216. package/dist/core/test-config.js.map +1 -1
  217. package/dist/core/ui-verify/index.d.ts +1 -1
  218. package/dist/core/ui-verify/index.d.ts.map +1 -1
  219. package/dist/core/ui-verify/index.js +2 -1
  220. package/dist/core/ui-verify/index.js.map +1 -1
  221. package/dist/core/ui-verify/smoke-engine.js +71 -15
  222. package/dist/core/ui-verify/smoke-engine.js.map +1 -1
  223. package/dist/core/ui-verify/spec-generator.d.ts +5 -9
  224. package/dist/core/ui-verify/spec-generator.d.ts.map +1 -1
  225. package/dist/core/ui-verify/spec-generator.js +66 -3
  226. package/dist/core/ui-verify/spec-generator.js.map +1 -1
  227. package/dist/core/ui-verify/types.d.ts +25 -0
  228. package/dist/core/ui-verify/types.d.ts.map +1 -1
  229. package/dist/core/ui-verify/visual-model-client.d.ts +2 -0
  230. package/dist/core/ui-verify/visual-model-client.d.ts.map +1 -1
  231. package/dist/core/ui-verify/visual-model-client.js +4 -4
  232. package/dist/core/ui-verify/visual-model-client.js.map +1 -1
  233. package/dist/core/unified-config.d.ts +27 -0
  234. package/dist/core/unified-config.d.ts.map +1 -1
  235. package/dist/core/unified-config.js +37 -0
  236. package/dist/core/unified-config.js.map +1 -1
  237. package/package.json +1 -1
  238. package/templates/accounts-example.yaml +49 -0
  239. package/templates/html/fonts/JetBrainsMono.woff2 +0 -0
  240. package/templates/html/fonts/Orbitron.woff2 +0 -0
  241. package/templates/html/speccore-ask-explain.html +1 -1
  242. package/templates/html/speccore-ask-guide.html +2 -2
  243. package/templates/html/speccore-ask-match.html +1 -1
  244. package/templates/html/speccore-ask-pipeline.html +1 -1
  245. package/templates/html/speccore-ask-result.html +2 -2
  246. package/templates/html/speccore-help.html +2 -2
  247. package/templates/html/speccore-setup-guide.html +16 -1
  248. package/templates/speccore-yml-example.yml +12 -0
@@ -0,0 +1,33 @@
1
+ /**
2
+ * visual-judge — AI 验证视觉判定兜底(v8.3.350+)
3
+ *
4
+ * 三级执行通道的末级兜底:宿主 AI 无多模态能力时,只操作页面截图、不判定,
5
+ * 在 results.json 中写 judged:false + screenshots。本模块用视觉模型(qwen-vl 默认)
6
+ * 对「截图 + 预期结果原文」做业务断言,回写判定结果。
7
+ *
8
+ * 判定原则:业务断言为主——截图是否符合 plan.json 中该场景的预期结果。
9
+ */
10
+ import type { VisualModelConfig } from '../ui-verify/visual-model-client';
11
+ import type { AiVerifyPlan, AiVerifyResult, AiVerifyResults } from './types';
12
+ /** 待判定场景(judged:false 且有截图) */
13
+ export interface PendingJudgement {
14
+ result: AiVerifyResult;
15
+ screenshotPaths: string[];
16
+ }
17
+ /** 找出 results 中待视觉判定的场景(judged 不为 true 且有截图) */
18
+ export declare function findPendingJudgements(results: AiVerifyResults): PendingJudgement[];
19
+ /**
20
+ * 用视觉模型判定单个待判定场景。
21
+ * @param result 待判定结果(会被改写)
22
+ * @param screenshotPaths 截图相对路径(相对 outDir)
23
+ * @param outDir 剧本输出目录
24
+ * @param plan 剧本(取场景标题与预期结果)
25
+ * @param config 视觉模型配置
26
+ */
27
+ export declare function judgeScenario(result: AiVerifyResult, screenshotPaths: string[], outDir: string, plan: AiVerifyPlan, config: VisualModelConfig): Promise<void>;
28
+ /**
29
+ * 兜底判定入口:对 results 中所有待判定场景逐个判定并回写。
30
+ * 返回判定数量;无待判定场景返回 0。
31
+ */
32
+ export declare function runAiJudge(resultsPath: string, plan: AiVerifyPlan, config?: VisualModelConfig): Promise<number>;
33
+ //# sourceMappingURL=visual-judge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visual-judge.d.ts","sourceRoot":"","sources":["../../../src/core/ai-verify/visual-judge.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAqB,MAAM,kCAAkC,CAAC;AAG7F,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE7E,+BAA+B;AAC/B,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,gDAAgD;AAChD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,eAAe,GAAG,gBAAgB,EAAE,CAIlF;AAiBD;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,cAAc,EACtB,eAAe,EAAE,MAAM,EAAE,EACzB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,YAAY,EAClB,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,IAAI,CAAC,CA6Cf;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,YAAY,EAClB,MAAM,CAAC,EAAE,iBAAiB,GACzB,OAAO,CAAC,MAAM,CAAC,CA8BjB"}
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ /**
3
+ * visual-judge — AI 验证视觉判定兜底(v8.3.350+)
4
+ *
5
+ * 三级执行通道的末级兜底:宿主 AI 无多模态能力时,只操作页面截图、不判定,
6
+ * 在 results.json 中写 judged:false + screenshots。本模块用视觉模型(qwen-vl 默认)
7
+ * 对「截图 + 预期结果原文」做业务断言,回写判定结果。
8
+ *
9
+ * 判定原则:业务断言为主——截图是否符合 plan.json 中该场景的预期结果。
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.findPendingJudgements = findPendingJudgements;
13
+ exports.judgeScenario = judgeScenario;
14
+ exports.runAiJudge = runAiJudge;
15
+ const path_1 = require("path");
16
+ const fs_extra_1 = require("fs-extra");
17
+ const visual_model_client_1 = require("../ui-verify/visual-model-client");
18
+ const logger_1 = require("../../utils/logger");
19
+ /** 找出 results 中待视觉判定的场景(judged 不为 true 且有截图) */
20
+ function findPendingJudgements(results) {
21
+ return results.results
22
+ .filter((r) => r.judged !== true && Array.isArray(r.screenshots) && r.screenshots.length > 0)
23
+ .map((r) => ({ result: r, screenshotPaths: r.screenshots || [] }));
24
+ }
25
+ /** 业务断言 prompt:预期结果 + 截图 → passed/analysis */
26
+ function buildJudgementPrompt(scenarioTitle, expected) {
27
+ const expectLines = expected.length > 0 ? expected.map((e) => `- ${e}`).join('\n') : '(无明确预期,请判断页面是否正常渲染、无明显错误)';
28
+ return `你是一个 Web 功能验收测试员。请判断「${scenarioTitle}」的页面截图是否符合以下预期结果:
29
+
30
+ 预期结果:
31
+ ${expectLines}
32
+
33
+ 请仅依据截图内容判定(无法从截图确认的事项视为不符合预期),并按以下 JSON 格式输出,不要输出其他内容:
34
+ {
35
+ "passed": true/false,
36
+ "analysis": "判定理由(中文,200字以内):截图中的哪些可见内容支持或否定预期"
37
+ }`;
38
+ }
39
+ /**
40
+ * 用视觉模型判定单个待判定场景。
41
+ * @param result 待判定结果(会被改写)
42
+ * @param screenshotPaths 截图相对路径(相对 outDir)
43
+ * @param outDir 剧本输出目录
44
+ * @param plan 剧本(取场景标题与预期结果)
45
+ * @param config 视觉模型配置
46
+ */
47
+ async function judgeScenario(result, screenshotPaths, outDir, plan, config) {
48
+ const scenario = plan.scenarios.find((s) => s.id === result.scenarioId);
49
+ const title = scenario?.title || result.scenarioId;
50
+ const expected = scenario?.expected || [];
51
+ // 依次尝试截图,取第一张可读的
52
+ let currentImageBase64 = null;
53
+ for (const shot of screenshotPaths) {
54
+ const abs = (0, path_1.isAbsolute)(shot) ? shot : (0, path_1.join)(outDir, shot);
55
+ try {
56
+ currentImageBase64 = (await (0, fs_extra_1.readFile)(abs)).toString('base64');
57
+ break;
58
+ }
59
+ catch {
60
+ continue;
61
+ }
62
+ }
63
+ if (!currentImageBase64) {
64
+ result.judged = true;
65
+ result.executor = 'visual-judge';
66
+ result.passed = false;
67
+ result.reason = '视觉判定失败:截图文件缺失或无法读取';
68
+ return;
69
+ }
70
+ let verdict;
71
+ try {
72
+ // 业务断言:用自定义 prompt 替代内置 UI 质量扫描(预期结果 + 截图 → passed/analysis)
73
+ verdict = await (0, visual_model_client_1.callVisualModel)(config, {
74
+ scenarioName: title,
75
+ currentImageBase64,
76
+ threshold: 'normal',
77
+ customPrompt: buildJudgementPrompt(title, expected),
78
+ });
79
+ }
80
+ catch (error) {
81
+ result.judged = true;
82
+ result.executor = 'visual-judge';
83
+ result.passed = false;
84
+ result.reason = `视觉模型调用失败: ${error.message || String(error)}`;
85
+ return;
86
+ }
87
+ result.executor = 'visual-judge';
88
+ result.judged = true;
89
+ result.passed = verdict.passed;
90
+ result.reason = verdict.analysis || (verdict.passed ? '截图符合预期' : '截图不符合预期');
91
+ }
92
+ /**
93
+ * 兜底判定入口:对 results 中所有待判定场景逐个判定并回写。
94
+ * 返回判定数量;无待判定场景返回 0。
95
+ */
96
+ async function runAiJudge(resultsPath, plan, config) {
97
+ const outDir = (0, path_1.dirname)(resultsPath);
98
+ const results = JSON.parse(await (0, fs_extra_1.readFile)(resultsPath, 'utf-8'));
99
+ const pending = findPendingJudgements(results);
100
+ if (pending.length === 0) {
101
+ logger_1.logger.info('ℹ️ 没有待视觉判定的场景(所有场景均已判定或缺少截图)');
102
+ return 0;
103
+ }
104
+ if (!config) {
105
+ logger_1.logger.error('❌ 未配置视觉模型(.speccore.yml 的 quality_gates.verify_ui.visual_model 或环境变量 DASHSCOPE_API_KEY)');
106
+ throw new Error('视觉模型不可用,无法执行 --ai-judge 兜底判定');
107
+ }
108
+ if (!(await (0, visual_model_client_1.isVisualModelAvailable)(config))) {
109
+ logger_1.logger.error('❌ 视觉模型不可用:未配置 API Key(DASHSCOPE_API_KEY / OPENAI_API_KEY / ANTHROPIC_API_KEY)');
110
+ throw new Error('视觉模型不可用,无法执行 --ai-judge 兜底判定');
111
+ }
112
+ logger_1.logger.info(`🤖 视觉判定兜底:${pending.length} 个场景待判定(provider: ${config.provider})`);
113
+ for (const item of pending) {
114
+ const before = item.result.passed;
115
+ await judgeScenario(item.result, item.screenshotPaths, outDir, plan, config);
116
+ const mark = item.result.passed ? '✅' : '❌';
117
+ logger_1.logger.info(` ${mark} ${item.result.scenarioId}(判定前 passed=${before})`);
118
+ }
119
+ // 判定结果回写 results.json(executor/judged/passed/reason 已被改写)
120
+ await (0, fs_extra_1.writeFile)(resultsPath, JSON.stringify(results, null, 2), 'utf-8');
121
+ return pending.length;
122
+ }
123
+ //# sourceMappingURL=visual-judge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visual-judge.js","sourceRoot":"","sources":["../../../src/core/ai-verify/visual-judge.ts"],"names":[],"mappings":"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"}
@@ -14,6 +14,12 @@ export interface CommandKnowledge {
14
14
  examples: string[];
15
15
  related: string[];
16
16
  triggers: string[];
17
+ /**
18
+ * 同分 tie-break 显式优先级(缺省 0,值大者赢)。
19
+ * 歧义词条(如「验证」同挂 verify/validate)靠此裁决,不依赖数组声明顺序;
20
+ * 新增词条若与现有词条共享触发词,必须显式声明 priority。
21
+ */
22
+ priority?: number;
17
23
  }
18
24
  /** Pipeline 步骤 */
19
25
  export interface PipelineStep {
@@ -1 +1 @@
1
- {"version":3,"file":"ask-engine.d.ts","sourceRoot":"","sources":["../../src/core/ask-engine.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAQ1E,aAAa;AACb,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,UAAU,GAAG,WAAW,CAAC;AAE/E,aAAa;AACb,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,kBAAkB;AAClB,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,kBAAkB;AAClB,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,aAAa;AACb,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,+BAA+B;IAC/B,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAChE,4CAA4C;IAC5C,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,eAAe;AACf,MAAM,WAAW,gBAAgB;IAC/B,iDAAiD;IACjD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAMD,QAAA,MAAM,UAAU,EAAE,gBAAgB,EAgEjC,CAAC;AA+EF,QAAA,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,CAgD7C,CAAC;AAMF,iBAAiB;AACjB,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAiEnD;AAy8BD,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,CAAC,CAkK7F;AAiQD,uCAAuC;AACvC,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CA4BjD;AAED,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;AAOjC;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,GAAG,cAAc,GAAG,IAAI,CAoB1G;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAiB7C"}
1
+ {"version":3,"file":"ask-engine.d.ts","sourceRoot":"","sources":["../../src/core/ask-engine.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAQ1E,aAAa;AACb,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,UAAU,GAAG,WAAW,CAAC;AAE/E,aAAa;AACb,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,kBAAkB;AAClB,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,kBAAkB;AAClB,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,aAAa;AACb,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,+BAA+B;IAC/B,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAChE,4CAA4C;IAC5C,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,eAAe;AACf,MAAM,WAAW,gBAAgB;IAC/B,iDAAiD;IACjD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AASD,QAAA,MAAM,UAAU,EAAE,gBAAgB,EAkEjC,CAAC;AA+EF,QAAA,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,CAgD7C,CAAC;AAMF,iBAAiB;AACjB,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAiEnD;AAshCD,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,CAAC,CAkK7F;AAiQD,uCAAuC;AACvC,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CA4BjD;AAED,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;AAOjC;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,GAAG,cAAc,GAAG,IAAI,CAoB1G;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAiB7C"}
@@ -24,6 +24,9 @@ const project_cards_1 = require("./project-cards");
24
24
  // ============================================================
25
25
  // 命令知识库
26
26
  // ============================================================
27
+ // ⚠️ 同分裁决规则(v8.3.351+ 显式化):
28
+ // 触发词总长度同分时按 priority 裁决(值大者赢,缺省 0);priority 也相同才回退声明顺序。
29
+ // 歧义词条必须显式设 priority(如 verify 与 validate 共享「验证」),禁止依赖声明顺序先到先得。
27
30
  const COMMAND_KB = [
28
31
  { name: 'init', aliases: ['in'], description: '初始化 SpecCore 项目,创建 .speccore 目录和配置',
29
32
  usage: 'speccore init [--interactive] [--name <name>]', examples: ['speccore init', 'speccore init --name my-project'], related: ['run', 'config'], triggers: ['初始化', 'init', '开始', '新建项目', '创建项目'] },
@@ -49,7 +52,9 @@ const COMMAND_KB = [
49
52
  usage: 'speccore pr [--task <id>] [--auto]', examples: ['speccore pr', 'speccore pr --task T-01 --auto'], related: ['done', 'execute'], triggers: ['pr', 'pull request', '提交', '合并', 'MR'] },
50
53
  // v8.3.95+: verify 命令
51
54
  { name: 'verify', aliases: ['vf'], description: '测试验证:冒烟测试 + 视觉检查 + API 契约 + 性能基线。支持 --headed 有头模式、--browser 多浏览器、--device 多设备',
52
- usage: 'speccore verify [--ui] [--smoke-only] [--visual-only] [--headed] [--browser <b>] [--device <d>] [--config <file>]', examples: ['speccore verify --ui', 'speccore verify --ui --headed', 'speccore verify --config ./tests/smoke.yaml'], related: ['deploy', 'execute', 'dashboard'], triggers: ['verify', '测试', '冒烟', 'smoke', '视觉检查', 'ui验证', '验证', '跑测试', '测一下', '检查页面'] },
55
+ usage: 'speccore verify [--ui] [--smoke-only] [--visual-only] [--headed] [--browser <b>] [--device <d>] [--config <file>]', examples: ['speccore verify --ui', 'speccore verify --ui --headed', 'speccore verify --config ./tests/smoke.yaml'], related: ['deploy', 'execute', 'dashboard'], triggers: ['verify', '测试', '冒烟', 'smoke', '视觉检查', 'ui验证', '验证', '跑测试', '测一下', '检查页面'],
56
+ // v8.3.351+: 「验证」同挂 verify/validate,测试/验证动作词义优先(显式分层,不靠声明顺序)
57
+ priority: 1 },
53
58
  { name: 'validate', aliases: ['vl'], description: '合规验证:检查 Spec 完整性与一致性',
54
59
  usage: 'speccore validate [--iteration <name>]', examples: ['speccore validate', 'speccore validate --iteration Q2'], related: ['analyze'], triggers: ['验证', 'validate', '检查', '合规', '校验'] },
55
60
  { name: 'sync', aliases: ['sy'], description: '双向同步:代码↔Spec,--global 同步到全局层(原 sync-global)',
@@ -323,13 +328,17 @@ function matchCommandInKB(input) {
323
328
  // 触发词匹配(最不可靠,需诚实声明低置信度)
324
329
  let best = null;
325
330
  let bestScore = 0;
331
+ let bestPriority = -Infinity;
326
332
  for (const cmd of COMMAND_KB) {
327
333
  // v8.3.257: 用匹配触发词的总长度加权——长词(如「全局分析」4字)比短词(「分析」2字)更具体,
328
334
  // 避免「全局分析」被 analyze 的泛触发词「分析」抢占
335
+ // v8.3.351+: 同分 tie-break 显式化——priority 值大者赢,priority 相同回退声明顺序
329
336
  const score = cmd.triggers.filter(t => lower.includes(t)).reduce((s, t) => s + t.length, 0);
330
- if (score > bestScore) {
337
+ const priority = cmd.priority ?? 0;
338
+ if (score > bestScore || (score === bestScore && priority > bestPriority)) {
331
339
  bestScore = score;
332
340
  best = cmd;
341
+ bestPriority = priority;
333
342
  }
334
343
  }
335
344
  if (bestScore > 0 && best) {
@@ -599,11 +608,59 @@ function formatCommandDisplay(r) {
599
608
  : r.command;
600
609
  }
601
610
  /** 模式3: 意图匹配(当前 ask 逻辑) */
611
+ /**
612
+ * v8.3.351+: 提取「合并/提交到 {环境}」句式的目标环境名(归一化英文标识)供 pr 意图透传 --base;
613
+ * 识别与解析分离——分支解析在 pr 命令内走 resolveEnvBranchRef(与 pr_target 环境名引用同链路)。
614
+ * 无环境词时返回空串(普通 pr 意图不加 --base)。
615
+ */
616
+ function extractMergeEnvTarget(input) {
617
+ const match = input.match(/(?:合并|merge|合到|合入|提交|commit)(?:到|至|入)?[^,。!?]{0,6}(local|dev|develop|test|staging|production|prod|预发布|预发|线上|生产|测试|开发|本地)(?:\s*环境)?/i);
618
+ if (!match)
619
+ return '';
620
+ const envMap = {
621
+ '预发布': 'staging', '预发': 'staging', '线上': 'production', '生产': 'production', 'prod': 'production',
622
+ '测试': 'test', '开发': 'dev', 'develop': 'dev', '本地': 'local',
623
+ };
624
+ return envMap[match[1].toLowerCase()] || match[1].toLowerCase();
625
+ }
602
626
  async function handleMatch(input, isExplicit = false) {
603
627
  // 补充分析意图已在 askEngine 顶层检测(第零层),此处不再重复
604
628
  // 优先用 KB 精确匹配
605
629
  const kbMatch = matchCommandInKB(input);
606
630
  const config = await (0, ask_config_1.loadAskConfig)();
631
+ // v8.3.351+: 「合并/提交到 {环境}」确定性短路——无论 KB 词条(「开发」抢 pr 先手)
632
+ // 还是意图识别(execute 宽触发词)谁得先机,该句式一律路由 pr --base <环境>;
633
+ // 环境名是目标环境,不是开发/测试动作词。「合并需求」无「到」不短路(走 analyze)。
634
+ const mergeEnvTarget = extractMergeEnvTarget(input);
635
+ if (mergeEnvTarget && /(?:合并|merge|合到|合入|提交)(?:到|至|入)/i.test(input)) {
636
+ const mergeResult = {
637
+ mode: 'match',
638
+ summary: `✅ 推荐: speccore pr --base ${mergeEnvTarget}`,
639
+ detail: `🎯 推荐命令: speccore pr --base ${mergeEnvTarget}\n创建 Pull Request:目标分支指向 ${mergeEnvTarget} 环境分支(pr 内解析 environments/${mergeEnvTarget}.yaml 的 branch)\n\n⚠️ 请确认后执行以上命令。`,
640
+ commands: ['pr'],
641
+ };
642
+ // 域检测(与 AMBIGUOUS_INTENTS 机制对齐):「合并到测试环境」也是日常讨论高频句,
643
+ // 分不清是 speccore 操作还是普通对话时先确认,不直接执行
644
+ if (!isExplicit && !(0, intent_recognition_1.isSpeccoreOperation)(input)) {
645
+ return {
646
+ mode: 'ambiguous',
647
+ summary: `待确认: pr — 是 speccore 操作还是普通对话?`,
648
+ detail: [
649
+ '🤔 我识别到你可能想执行 speccore 命令,但不确定这是调用 CLI 还是普通技术讨论:',
650
+ '',
651
+ ` 你的输入: "${input}"`,
652
+ ` 匹配意图: pr → speccore pr --base ${mergeEnvTarget}(目标分支指向该环境分支)`,
653
+ '',
654
+ '如果是 speccore 操作(创建 PR 并指向该环境分支),请输入 y 确认;',
655
+ '如果是普通技术讨论(如讨论代码流向/分支策略),请直接描述你的问题。',
656
+ '',
657
+ '或输入 y 确认执行: speccore pr --base ' + mergeEnvTarget,
658
+ ].join('\n'),
659
+ commands: ['pr'],
660
+ };
661
+ }
662
+ return mergeResult;
663
+ }
607
664
  const results = await (0, intent_recognition_1.recognizeIntent)(input);
608
665
  let best = results[0];
609
666
  // v8.3.191+: run 意图带 preset/起止点(extractParams 已精确识别编排)时优先 run 编排,
@@ -638,8 +695,11 @@ async function handleMatch(input, isExplicit = false) {
638
695
  }
639
696
  // 如果 KB 有匹配且置信度高于意图识别,用 KB(但要整合意图识别的参数)
640
697
  // v8.3.191+: run 意图有 preset/起止点时,优先 run 编排(避免被 KB 的 split/deploy 等抢占)
698
+ // v8.3.351+: KB 命中 pr 且句中提取到目标环境(「合并到开发环境」)时强制走 KB 分支——
699
+ // 「开发」是环境名不是开发动作,不得被 execute 的宽触发词抢占
641
700
  const runHasParams = best?.command === 'run' && (best.extractedParams?.preset || best.extractedParams?.from || best.extractedParams?.to || best.extractedParams?.gate);
642
- if (kbMatch && (!best || best.confidence < 60) && !runHasParams) {
701
+ const prWithEnvTarget = kbMatch?.name === 'pr' && extractMergeEnvTarget(input) !== '';
702
+ if (kbMatch && (!best || best.confidence < 60 || prWithEnvTarget) && !runHasParams) {
643
703
  const params = best?.extractedParams || {};
644
704
  let fullCommand = `speccore ${kbMatch.name}`;
645
705
  // v8.3.301+: from-source 端名自动映射的备注,等 paramNotes 声明后再 append
@@ -723,6 +783,16 @@ async function handleMatch(input, isExplicit = false) {
723
783
  fullCommand += ` --prompt`;
724
784
  paramNotes.push(`🧠 模式: AI 智能拆分`);
725
785
  }
786
+ // v8.3.351+: KB trigger「合并」命中 pr 时,提取目标环境透传 --base——
787
+ // pr 命令内 resolveEnvBranchRef 解析为 environments/{env}.yaml 的 branch;
788
+ // 「合并到测试环境」在 COMMAND_MAPPINGS 无 pr 触发词(best 为空),必走本 KB 分支
789
+ if (kbMatch.name === 'pr') {
790
+ const mergeEnv = extractMergeEnvTarget(input);
791
+ if (mergeEnv) {
792
+ fullCommand += ` --base ${mergeEnv}`;
793
+ paramNotes.push(`🌿 目标环境: ${mergeEnv} → PR 目标分支指向该环境分支`);
794
+ }
795
+ }
726
796
  // 补充分析意图已在 askEngine 顶层检测,此处不再重复
727
797
  let detail = `🎯 推荐命令: ${fullCommand}\n${kbMatch.description}\n\n用法: ${kbMatch.usage}`;
728
798
  if (paramNotes.length > 0)
@@ -881,6 +951,16 @@ async function handleMatch(input, isExplicit = false) {
881
951
  // v8.3.257: search 用位置参数而非 -n(search 无 -n 选项,避免死参数)
882
952
  fullCommand = `speccore search "${params.name || input.slice(0, 40)}"`;
883
953
  }
954
+ else if (best.command === 'pr') {
955
+ fullCommand = `speccore pr`;
956
+ // v8.3.351+: 高置信度 pr 意图同样支持「合并到 {环境}」环境名透传(KB 分支的对称兜底);
957
+ // 「需求合并」等语料由 SYNONYM_MAP 优先路由 analyze,不会落到此处
958
+ const mergeEnv = extractMergeEnvTarget(input);
959
+ if (mergeEnv) {
960
+ fullCommand += ` --base ${mergeEnv}`;
961
+ paramNotes.push(`🌿 目标环境: ${mergeEnv} → PR 目标分支指向该环境分支`);
962
+ }
963
+ }
884
964
  else {
885
965
  fullCommand = `speccore ${best.command}`;
886
966
  // v8.3.257: -n/--iter 不是通用选项——仅 task new/iteration create 注册了 -n,