paseo-req-kit 1.13.0-beta.1 → 1.13.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/bin/bootstrap.d.mts +1 -0
  3. package/bin/bootstrap.mjs +31 -5
  4. package/bin/req.mjs +9 -0
  5. package/contracts/cli-json.md +2 -0
  6. package/docs/adr/0033-prototype-review-and-ui-check.md +28 -0
  7. package/docs/adr/README.md +1 -0
  8. package/docs/guide//344/275/277/347/224/250/350/200/205/346/214/207/345/215/227.md +8 -0
  9. package/docs/guide//347/273/264/346/212/244/350/200/205/346/214/207/345/215/227.md +1 -1
  10. package/docs/guide//351/241/271/347/233/256/345/256/232/345/210/266/344/270/216/350/277/220/350/241/214/350/247/204/345/210/231.md +7 -1
  11. package/engine/prompts/03-design.md +5 -1
  12. package/engine/prompts/04b-design-revise.md +1 -1
  13. package/engine/prompts/browser-verify.md +2 -2
  14. package/engine/prompts/ui-check.md +15 -0
  15. package/engine/src/api.ts +17 -2
  16. package/engine/src/config-view.ts +1 -1
  17. package/engine/src/pipeline.ts +15 -7
  18. package/engine/src/review-assets/app.css +420 -0
  19. package/engine/src/review-assets/app.html +95 -0
  20. package/engine/src/review-assets/app.js +954 -0
  21. package/engine/src/review.ts +570 -0
  22. package/engine/src/ui-check-page.js +208 -0
  23. package/engine/src/ui-check.ts +366 -0
  24. package/package.json +1 -1
  25. package/paseo-plugin/client/data.ts +12 -1
  26. package/paseo-plugin/client/detail.tsx +3 -0
  27. package/paseo-plugin/client/inbox.tsx +2 -0
  28. package/paseo-plugin/client/review-link.tsx +29 -0
  29. package/paseo-plugin/index.server.ts +2 -1
  30. package/paseo-plugin/package.json +1 -1
  31. package/paseo-plugin/server/handlers.ts +7 -1
  32. package/paseo-plugin/shared/contracts.ts +9 -0
  33. package/site-dist/404.html +2 -2
  34. package/site-dist/assets/{app.C_2Vhmv-.js → app.DWPPPhFm.js} +1 -1
  35. package/site-dist/assets/changelog.md.CoelFFCL.js +1 -0
  36. package/site-dist/assets/chunks/@localSearchIndexroot.Hdq0-4L3.js +1 -0
  37. package/site-dist/assets/chunks/{VPLocalSearchBox.zPMXlb4V.js → VPLocalSearchBox.D3_9dRGY.js} +1 -1
  38. package/site-dist/assets/chunks/{theme.DRjHgCIR.js → theme.Bm8aLiLN.js} +2 -2
  39. package/site-dist/assets/{guide_getting-started.md.DjqrOQ_8.js → guide_getting-started.md.B4GJeoPz.js} +1 -1
  40. package/site-dist/assets/{guide_getting-started.md.DjqrOQ_8.lean.js → guide_getting-started.md.B4GJeoPz.lean.js} +1 -1
  41. package/site-dist/assets/guide_maintainer.md.RZIzyCTV.js +1 -0
  42. package/site-dist/assets/{guide_maintainer.md.C7Aav_lk.lean.js → guide_maintainer.md.RZIzyCTV.lean.js} +1 -1
  43. package/site-dist/assets/{guide_project.md.r2g5uU_4.js → guide_project.md.nGlTCo4D.js} +2 -2
  44. package/site-dist/assets/{guide_project.md.r2g5uU_4.lean.js → guide_project.md.nGlTCo4D.lean.js} +2 -2
  45. package/site-dist/assets/{guide_user.md.FpCmz9S0.js → guide_user.md.tiHFxIsQ.js} +1 -1
  46. package/site-dist/assets/guide_user.md.tiHFxIsQ.lean.js +1 -0
  47. package/site-dist/assets/reference_adr_0033-prototype-review-and-ui-check.md.hFcQzmaa.js +1 -0
  48. package/site-dist/assets/reference_adr_0033-prototype-review-and-ui-check.md.hFcQzmaa.lean.js +1 -0
  49. package/site-dist/assets/{reference_adr_index.md.DTVyIA6V.js → reference_adr_index.md.BYF-Ofor.js} +1 -1
  50. package/site-dist/assets/{reference_adr_index.md.DTVyIA6V.lean.js → reference_adr_index.md.BYF-Ofor.lean.js} +1 -1
  51. package/site-dist/assets/{reference_contracts_cli-json.md.gkER6rX7.js → reference_contracts_cli-json.md.N-v2HNUd.js} +1 -1
  52. package/site-dist/assets/{reference_contracts_cli-json.md.gkER6rX7.lean.js → reference_contracts_cli-json.md.N-v2HNUd.lean.js} +1 -1
  53. package/site-dist/changelog.html +5 -5
  54. package/site-dist/guide/getting-started.html +5 -5
  55. package/site-dist/guide/how-it-works.html +4 -4
  56. package/site-dist/guide/index.html +4 -4
  57. package/site-dist/guide/install.html +4 -4
  58. package/site-dist/guide/maintainer.html +5 -5
  59. package/site-dist/guide/phases.html +4 -4
  60. package/site-dist/guide/project.html +6 -6
  61. package/site-dist/guide/user.html +5 -5
  62. package/site-dist/hashmap.json +1 -1
  63. package/site-dist/index.html +4 -4
  64. package/site-dist/reference/adr/0001-step-agents-under-origin-chat.html +4 -4
  65. package/site-dist/reference/adr/0002-no-directory-switch-in-chat.html +4 -4
  66. package/site-dist/reference/adr/0003-script-engine-not-hub.html +4 -4
  67. package/site-dist/reference/adr/0004-result-files.html +4 -4
  68. package/site-dist/reference/adr/0005-no-default-branch-local-commits-only.html +4 -4
  69. package/site-dist/reference/adr/0006-skill-install-targets.html +4 -4
  70. package/site-dist/reference/adr/0007-own-interview-skill.html +4 -4
  71. package/site-dist/reference/adr/0008-entries-do-not-call-entries.html +4 -4
  72. package/site-dist/reference/adr/0009-versioned-install-layout.html +4 -4
  73. package/site-dist/reference/adr/0010-generated-feature-overview.html +4 -4
  74. package/site-dist/reference/adr/0011-resume-after-completion.html +4 -4
  75. package/site-dist/reference/adr/0012-review-panel.html +4 -4
  76. package/site-dist/reference/adr/0013-no-release-tooling.html +4 -4
  77. package/site-dist/reference/adr/0014-notifier-slot.html +4 -4
  78. package/site-dist/reference/adr/0015-incremental-continue.html +4 -4
  79. package/site-dist/reference/adr/0016-upgrade-from-tags.html +4 -4
  80. package/site-dist/reference/adr/0017-use-paseo-capabilities.html +4 -4
  81. package/site-dist/reference/adr/0018-own-worktree-option.html +4 -4
  82. package/site-dist/reference/adr/0019-parallel-orchestration.html +4 -4
  83. package/site-dist/reference/adr/0020-scheduling-through-the-chat.html +4 -4
  84. package/site-dist/reference/adr/0021-npm-version-manager.html +4 -4
  85. package/site-dist/reference/adr/0022-develop-and-verify-phases.html +4 -4
  86. package/site-dist/reference/adr/0023-types-gate-slices.html +4 -4
  87. package/site-dist/reference/adr/0024-confirm-early-review-axes.html +4 -4
  88. package/site-dist/reference/adr/0025-joint-requirements.html +4 -4
  89. package/site-dist/reference/adr/0026-machine-records-and-api.html +4 -4
  90. package/site-dist/reference/adr/0027-paseo-plugin.html +4 -4
  91. package/site-dist/reference/adr/0028-external-integrations.html +4 -4
  92. package/site-dist/reference/adr/0029-requirements-from-cards.html +4 -4
  93. package/site-dist/reference/adr/0030-card-intake-process.html +4 -4
  94. package/site-dist/reference/adr/0031-fewer-interventions.html +4 -4
  95. package/site-dist/reference/adr/0032-npm-only-install.html +4 -4
  96. package/site-dist/reference/adr/0033-prototype-review-and-ui-check.html +30 -0
  97. package/site-dist/reference/adr/index.html +5 -5
  98. package/site-dist/reference/commands.html +4 -4
  99. package/site-dist/reference/config.html +4 -4
  100. package/site-dist/reference/contracts/cli-json.html +5 -5
  101. package/site-dist/reference/contracts/feature-files.html +4 -4
  102. package/site-dist/reference/contracts/index.html +4 -4
  103. package/site-dist/reference/contracts/notifier-event.html +4 -4
  104. package/site-dist/reference/contracts/result-file.html +4 -4
  105. package/site-dist/reference/contracts/spec.html +4 -4
  106. package/site-dist/reference/contracts/state.html +4 -4
  107. package/site-dist/reference/contracts/usage-record.html +4 -4
  108. package/site-dist/reference/contracts/verify.html +4 -4
  109. package/site-dist/reference/scope.html +4 -4
  110. package/site-dist/assets/changelog.md.VHiXRXap.js +0 -1
  111. package/site-dist/assets/chunks/@localSearchIndexroot.hC_y1UNZ.js +0 -1
  112. package/site-dist/assets/guide_maintainer.md.C7Aav_lk.js +0 -1
  113. package/site-dist/assets/guide_user.md.FpCmz9S0.lean.js +0 -1
  114. /package/site-dist/assets/{changelog.md.VHiXRXap.lean.js → changelog.md.CoelFFCL.lean.js} +0 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  每个版本分为"使用者会看到的变化"和"升级须知"。升级步骤见 [docs/guide/安装与升级.md](docs/guide/安装与升级.md)。
4
4
 
5
+ ## 1.13.0-beta.2
6
+
7
+ 在 Paseo 网页端审查原型,界面验证不再依赖桌面端和模型的视觉能力。见 [ADR 0033](docs/adr/0033-prototype-review-and-ui-check.md)。
8
+
9
+ ### 使用者会看到的变化
10
+
11
+ - **原型审查页**:界面改动的需求请你确认方案时,在插件的需求页面或"等你处理"里点"打开原型审查页"(终端:`req review <需求>`)。在原型上点要改的地方,选一种改法(文字改成…、大一点、换颜色、换位置、删掉、这里加东西……),用自己的话补一句;双击文字直接改字;切换正常、出错、空数据等状态和电脑、手机宽度。被点元素的文字、颜色、大小、位置和对应的验收标准自动记下,提交后整理成清单转给需求会话;没有要改的,直接同意开始写代码。意见自动保存;原型改过后再打开,会标出这一版改了哪里。
12
+ - **界面检查 `req ui-check`**:用本机的 Edge 或 Chrome 打开页面,按电脑和手机宽度用文字报告文字显示不全、互相遮挡、超出屏幕、图片没加载、文字太淡、控件没有名字、控制台报错、请求失败,并和确认过的原型逐项对标题、按钮、字段名、表头。功能验证、缺陷修复和复验自动使用;不需要 Paseo 桌面端,不需要模型看图。截图只存成文件给人看,不再读进对话,省 token。
13
+ - 安装时 Paseo daemon 启动得慢不再判为失败;真的没启动时写出 Paseo 报的原因和日志位置。
14
+
15
+ ### 升级须知
16
+
17
+ - 方案设计的原型按新约定写:改动区域标 `data-ac`、`data-label`,状态包在 `<section data-state="…">` 中,原型里不写脚本(审查页不会运行它们)。
18
+ - `req ui-check` 第一次运行时从 npm 源安装 `playwright-core` 到 `~/.req-kit/tools`(不下载浏览器);本机要有 Edge 或 Chrome,或者用 `--browser` / `REQ_KIT_BROWSER` 指定浏览器程序。
19
+ - 原型审查页的服务默认只给本机访问。需求方从别的电脑访问 Paseo 网页端时,见《项目定制与运行规则》"方案确认点"一节的 `REQ_KIT_REVIEW_*` 环境变量。
20
+ - `browserVerify: "off"` 现在同时关闭界面检查。
21
+
5
22
  ## 1.13.0-beta.1
6
23
 
7
24
  公司内网只有 npm 镜像、打不开 GitHub 和文档站点时,也能一条命令装好、升级和看文档。见 [ADR 0032](docs/adr/0032-npm-only-install.md)。
@@ -10,3 +10,4 @@ export function parseArgs(argv: string[]): {
10
10
  };
11
11
  export function sameRegistry(a: string | null | undefined, b: string | null | undefined): boolean;
12
12
  export function which(name: string, env?: NodeJS.ProcessEnv | Record<string, string>, skipRoot?: string | null): string | null;
13
+ export function startProblem(result: { status: number | null; stdout: string; stderr: string; error?: (Error & { code?: string }) | undefined }): string;
package/bin/bootstrap.mjs CHANGED
@@ -188,6 +188,18 @@ async function daemonStatus(url) {
188
188
  }
189
189
  }
190
190
 
191
+ /** The useful lines of a failed `paseo daemon start`: its error and the log path it names. */
192
+ export function startProblem(result) {
193
+ if (result.error?.code === "ETIMEDOUT") return "等了 11 分钟仍没有就绪";
194
+ if (result.error) return result.error.message;
195
+ const lines = `${result.stderr}\n${result.stdout}`
196
+ .split("\n")
197
+ .map((l) => l.trim())
198
+ .filter(Boolean);
199
+ const pick = lines.filter((l) => /error|fail|logs?:|EADDRINUSE|denied|not ready/i.test(l));
200
+ return (pick.length > 0 ? pick : lines).slice(0, 4).join(" / ").slice(0, 600);
201
+ }
202
+
191
203
  async function ask(question) {
192
204
  const rl = createInterface({ input: process.stdin, output: process.stdout });
193
205
  try {
@@ -321,12 +333,26 @@ async function main(argv) {
321
333
  if ((await daemonStatus(daemonUrl)).up) {
322
334
  if (changed) warn("Paseo daemon 正在运行,网页界面要在重启后生效:paseo daemon restart(会中断正在运行的 agent,方便时再重启)");
323
335
  else ok("Paseo daemon 正在运行");
324
- } else if (paseo(["daemon", "start"]).status === 0) {
336
+ } else {
337
+ // `paseo daemon start` waits until the daemon listens (up to 10 minutes; the first start
338
+ // on a new machine can take a while), so it gets more time than other paseo calls.
339
+ say(" 正在启动 Paseo daemon(第一次启动可能要一两分钟)……");
340
+ const started = paseo(["daemon", "start"], { timeout: 660_000 });
325
341
  // Wait for it to answer: the wizard below asks the daemon for the agents.
326
- const wait = Number(process.env.REQ_KIT_PASEO_WAIT ?? 15);
327
- for (let i = 0; i < wait && !(await daemonStatus(daemonUrl)).up; i++) await new Promise((r) => setTimeout(r, 1000));
328
- ok("已启动 Paseo daemon");
329
- } else warn("没能启动 Paseo daemon,请运行 paseo daemon start 查看原因");
342
+ const wait = Number(process.env.REQ_KIT_PASEO_WAIT ?? 30);
343
+ let up = (await daemonStatus(daemonUrl)).up;
344
+ for (let i = 0; i < wait && !up; i++) {
345
+ await new Promise((r) => setTimeout(r, 1000));
346
+ up = (await daemonStatus(daemonUrl)).up;
347
+ }
348
+ if (up) ok("已启动 Paseo daemon");
349
+ else {
350
+ const why = startProblem(started);
351
+ if (started.status === 0) warn(`已运行 paseo daemon start,但 ${daemonUrl} 还没有响应${why ? `:${why}` : ""}`);
352
+ else warn(`没能启动 Paseo daemon${why ? `:${why}` : ""}`);
353
+ say(" 稍后运行 paseo daemon status 查看;还是没有启动时运行 paseo daemon start,日志在它写出的 Logs 路径。不影响这次安装,启动后再运行 req doctor");
354
+ }
355
+ }
330
356
  say(` Paseo 网页界面:${daemonUrl}/`);
331
357
  }
332
358
 
package/bin/req.mjs CHANGED
@@ -30,6 +30,9 @@ const HELP = `req-kit ${version}(${kitRoot})
30
30
  req digest --schedule "0 9 * * 1"
31
31
  让 Paseo 按时间表发送需求总览(例子为每周一 9:00);取消:req digest --cancel <ID>
32
32
  req sync <需求> 把需求同步到对接的外部系统(补发失败的调用、补建卡片、更新状态)
33
+ req review <需求> 在浏览器打开原型审查页:点选要改的地方、直接改文字,提交意见或确认方案
34
+ req ui-check <网址> [--prototype <原型文件>] [--width 1280,390]
35
+ 不靠看图检查网页:显示不全、遮挡、超出屏幕、文字太淡、报错,和原型对文字
33
36
  req reply <需求> <回复> 把回答或方案意见转给需求的对话(插件的"等你处理"用它)
34
37
  req integration test [需求] 只显示对接外部系统时会执行的命令,不真正调用
35
38
  req start --from <系统>:<卡号> 从需求管理系统中的一张卡片开始需求(没有人开着对话也可以)
@@ -215,6 +218,12 @@ switch (command) {
215
218
  case "integration":
216
219
  code = needTsx() ? node([tsxCli, path.join(kitRoot, "engine", "src", "integration-cmd.ts"), ...rest]) : 1;
217
220
  break;
221
+ case "review":
222
+ code = needTsx() ? node([tsxCli, path.join(kitRoot, "engine", "src", "review.ts"), ...rest]) : 1;
223
+ break;
224
+ case "ui-check":
225
+ code = needTsx() ? node([tsxCli, path.join(kitRoot, "engine", "src", "ui-check.ts"), ...rest]) : 1;
226
+ break;
218
227
  case "docs":
219
228
  code = node([path.join(kitRoot, "bin", "req-docs.mjs"), ...rest]);
220
229
  break;
@@ -31,6 +31,7 @@
31
31
  | `req api models` | 1.12 起。`{ current, choices, recommended }`:本机配置中现在的 `agents`;daemon 上能无人值守运行的选择(`{ id, label, provider, profile }`,Paseo profile 在前);按推荐的 `agents`。连不上 daemon 时报错 |
32
32
  | `req api config [--repo <目录>] [--machine]` | 1.12 起。`{ sources, ignored, error, repo, groups }`:`sources` 是生效的配置文件(`layer` 为 `machine` / `team` / `project`,按叠加顺序);`groups` 按主题分组,每项 `{ key, label, description, display, value, setBy, scope, changed }`:`display` 是用大白话写的值,`value` 中看起来像密钥的值和个人账号替换为 `••••`,`setBy` 是写了它的文件,`changed` 表示与默认值不同。配置有错误时 `error` 写明原因,值按各文件所写。`--machine` 不看项目配置 |
33
33
  | `req api set-agents --json <设置>` | 1.12 起。`{ ok, message }`。`<设置>` 是 `{ builder, reviewer, fallback }`(`fallback` 可以为 null),检查后写入本机配置的 `agents`,原文件备份 |
34
+ | `req api review-url <需求> [--repo <目录>] [--read-only]` | 1.13 起。`{ url, readOnly }`:这个需求的原型审查页地址(需要时在后台启动审查页的服务)。`--read-only` 的地址只能看,不能提交意见或确认方案。需求不在本机的需求索引中或还没有 `prototype/` 时报 `not_found` |
34
35
 
35
36
  `<需求>` 的写法与 `req status` 相同:功能目录、目录名或名称中能唯一确定的一部分。需求在为它新建的工作区中开发时,`--repo` 写对话所在的目录或那个工作区都可以。
36
37
 
@@ -56,6 +57,7 @@
56
57
  | `updatedAt` | 最近一次变化的时间 |
57
58
  | `usage` | `{ steps, agentMinutes, inputTokens, outputTokens, costUsd }`;没运行过为 null |
58
59
  | `present` | 目录是否还在 |
60
+ | `hasPrototype` | 1.13 起。功能目录中有界面原型(`prototype/`),可以用原型审查页查看 |
59
61
 
60
62
  ## 详情(`status`)
61
63
 
@@ -0,0 +1,28 @@
1
+ # 0033 原型审查页和不靠看图的界面检查
2
+
3
+ - 状态:已采纳(1.13.0-beta.2)
4
+
5
+ ## 背景
6
+
7
+ 内网的使用者有三个限制:
8
+
9
+ - 只能用 Paseo 网页端(桌面端有安装限制),所以 Paseo 内置浏览器和它的 `browser_*` 工具用不了,1.8 起的"浏览器验证"实际上从不运行。
10
+ - 内网模型看图能力弱:截图交给它判断界面对不对,效果差,每张图还要消耗不少 token。
11
+ - 需求方多数不是开发者,确认方案时看得出"不对",但说不清哪里不对、要改成什么;插件里只有一个文本框写修改意见。插件页面也不能显示 HTML,原型只能在功能目录里自己找文件打开。
12
+
13
+ ## 决定
14
+
15
+ - **原型审查页**:一个本机网页(`engine/src/review.ts`),插件和 `req review` 打开它。需求方在原型上点选、选改法、补一句话,页面记下被点元素的文字、颜色、字号、大小、所在区域和对应的 AC;双击可以直接改文字。提交时整理成一份带位置信息的清单,经已有的 `req reply` 转给需求会话,确认方案经 `req approve`:不增加新的流程分支。
16
+ - 原型由 agent 生成,内容可能来自卡片等外部文字,和提交接口同源。所以原型以 `script-src 'none'` 返回,里面的脚本不运行,审查页从外面读写它的 DOM。
17
+ - 服务只监听 `127.0.0.1`,地址带随机令牌;另有一个只读令牌。远程访问要显式设置环境变量。
18
+ - 每次打开时保存原型的版本,改过后用文字差异标出变化,需求方不用自己找改了哪里。
19
+ - 方案设计的提示词规定原型的写法(`data-ac`、`data-label`、`data-state`、真实文字),审查页和界面检查都依赖它。
20
+ - **界面检查 `req ui-check`**:用 `playwright-core` 驱动本机的 Edge 或 Chrome(不下载浏览器,驱动在第一次使用时从 npm 源装到 `~/.req-kit/tools`),在页面里运行一段固定的脚本,用文字报告看得出的版面问题,并拿原型里的标题、按钮、字段名、表头和页面对文字。结果是确定的,不依赖模型看图。截图只存文件给人看。验证类步骤总是附上它;有 Paseo 浏览器时,同一段脚本也可以用 `browser_evaluate` 运行。
21
+ - 对比度只报明显看不清的(低于 3:1,大字 2.5:1),不报 WCAG 4.5:1 的边缘情况:常见组件库的主按钮正好在这个边缘,全报会淹没真正的问题。
22
+
23
+ ## 影响
24
+
25
+ - 需求方确认方案的方式从"写一段话"变成"在原型上点",修改意见能直接定位到元素。效果要看原型是否按约定标了区域。
26
+ - 验证不再依赖桌面端和模型的视觉能力;需要本机有 Edge 或 Chrome(Windows 自带 Edge)。
27
+ - `req ui-check` 第一次运行要能从 npm 源取到 `playwright-core`,内网走镜像。
28
+ - 只检查"看得出的"问题,不判断设计是否好看;"和原型对文字"只比较标签类文字,原型中的示例数据不比较。
@@ -34,3 +34,4 @@ req-kit 自身的设计决定。修改其中任何一条,请新增一条 ADR
34
34
  - [0030 接单器:共享机器上定时接卡片、转达回复](0030-card-intake-process.md)
35
35
  - [0031 少等人:每种中断都有自动的下一步](0031-fewer-interventions.md)
36
36
  - [0032 安装、升级和文档只依赖 npm 源](0032-npm-only-install.md)
37
+ - [0033 原型审查页和不靠看图的界面检查](0033-prototype-review-and-ui-check.md)
@@ -64,6 +64,14 @@ req-kit 帮你把一个需求从"我想要……"做到"可以验收":你在 P
64
64
 
65
65
  1.12 起,首页最上面是"等你处理":所有等你确认方案、回答问题、中断了的需求,等得最久的在前,每条写明要做什么、等了多久。方案可以直接确认,也可以写修改意见;方案审核的问题可以直接写回答,插件会转给这个需求的对话,效果和在对话里回答一样。首页的"配置"列出 req-kit 的全部设置:每一项是什么、现在的值、来自本机、团队还是项目的配置文件,可以按项目查看、搜索、只看改过的。
66
66
 
67
+ **在原型上提意见(1.13 起)。** 界面改动的需求,在请你确认方案时会有一份界面原型。在插件的需求页面或"等你处理"里点"打开原型审查页"(或在终端运行 `req review <需求>`),会在浏览器里打开它:
68
+
69
+ - 标着 ①②③ 的是这次要改的地方,右边写着各自对应的验收标准,顺着看就不会漏。
70
+ - 想改哪里就**点哪里**,选一种改法(文字改成…、大一点、换颜色、换位置、删掉、这里加东西……),再用自己的话补一句。不用想专业词汇,你点的是哪个按钮、原来是什么颜色和大小,页面会自动记下来交给 AI。
71
+ - 想换文字就**双击文字**,直接改成你想要的字。
72
+ - 正常、出错、空数据这些状态,以及电脑、手机宽度,在页面上方切换。
73
+ - 写的意见会自动保存。看完后点"提交修改意见",需求会话会据此改需求说明,改好后请你再确认;改过的原型再打开时,会用绿色框标出这一版改了哪里。没有要改的,点"同意开始写代码"。
74
+
67
75
  每个步骤都会出现在这个对话的子 agent 列表里(名字形如"[20261012-sms-login] 6-代码评审 第2轮"),点开可以看到它在做什么。功能目录下的 `progress.md` 按时间记录了每一步的结果。
68
76
 
69
77
  可能收到的通知(团队接入了企业 IM 的话,这些通知也会发到 IM):
@@ -39,7 +39,7 @@
39
39
  3. **写 `CHANGELOG.md`**:新增一节,分"使用者会看到的变化"和"升级须知"两部分。升级须知写清:要不要重跑 `/req-setup`、提示词有哪些变化(影响用 `prompts.local/` 覆盖过的人)、文件格式有没有变化、旧行为的兼容方式。
40
40
  4. **验证**:`npm test`、`npm run typecheck`、`npm run eval:self-check`;步骤提示词或引擎有改动时跑 `npm run eval`,把 `summary.md` 复制到 `evals/baselines/<版本>.md`;入口或手册有改动时跑对话评估,记入记分表。主版本发布前,在一个真实项目里试运行一周。
41
41
  5. **冒烟测试**:在一台干净的机器(或临时的 `REQ_KIT_HOME` 和 HOME)上按 [安装与升级](安装与升级.md) 安装,跑冒烟测试第 1–4 项;至少有一台是 Windows。
42
- 6. **合并到 `main`,标签自动打上**:`main` 收到 `package.json` 版本号还没有标签的提交时,`.github/workflows/release.yml` 先在这个提交上跑测试、类型检查和评估自检,通过后才打上同名标签(不用自己打,也不需要推送标签的权限),接着在同一次运行里发布。测试没通过时不会留下标签,修好后用同一个版本号再合一次即可(1.12.1 起)。也可以照旧自己打:在 GitHub 上建 Release 并新建标签 `<版本>`,或推送 `git tag -a <版本>`,同样会触发发布。发布流程:跑测试、类型检查和评估自检,核对标签与 `package.json` 版本一致,然后发布到 npm(预发布版用 `beta` 标签,正式版用 `latest`),正式版再设置 `paseo-<小版本>` 标签,把 `npm pack` 的安装包附在 Release 上,Release 正文取自 CHANGELOG 的这一节。标签同时也是 git 方式安装的版本列表,所以改版本号的提交要在验证完成后再合到 `main`(自动打标签的时机就是它);打错了尽快删除标签,npm 上发布错的版本用 `npm deprecate` 标注(npm 不允许重新发布同一版本号)。
42
+ 6. **合并到 `main`,标签自动打上**:`main` 收到 `package.json` 版本号还没有标签的提交时,`.github/workflows/release.yml` 先在这个提交上跑测试、类型检查和评估自检,通过后才打上同名标签(不用自己打,也不需要推送标签的权限),接着在同一次运行里发布。测试没通过时不会留下标签,把修复合到 `main` 就会用同一个版本号重新发布(1.12.1 起;1.13 起任何推到 `main` 的提交都会检查,不必再改 `package.json`)。也可以照旧自己打:在 GitHub 上建 Release 并新建标签 `<版本>`,或推送 `git tag -a <版本>`,同样会触发发布。发布流程:跑测试、类型检查和评估自检,核对标签与 `package.json` 版本一致,然后发布到 npm(预发布版用 `beta` 标签,正式版用 `latest`),正式版再设置 `paseo-<小版本>` 标签,把 `npm pack` 的安装包附在 Release 上,Release 正文取自 CHANGELOG 的这一节。标签同时也是 git 方式安装的版本列表,所以改版本号的提交要在验证完成后再合到 `main`(自动打标签的时机就是它);打错了尽快删除标签,npm 上发布错的版本用 `npm deprecate` 标注(npm 不允许重新发布同一版本号)。
43
43
  7. **通知**:把"使用者会看到的变化"发给使用者,把"升级须知"发给各台机器的安装者;团队配置的 `requiredVersion` 需要提高时,在团队配置仓库中修改。
44
44
 
45
45
  **npm 发布的一次性设置**:可信发布(trusted publisher)要在 npm 上已有的包的设置页里配置,所以第一个版本在本机手工发布一次,不需要生成任何口令:
@@ -43,6 +43,12 @@ spec 头部的 `- 类型:` 决定流水线走哪种做法(1.8 起)。缺
43
43
 
44
44
  等待确认时进度显示"等待需求方确认方案",需求总览归入"需要处理",外部通知收到 `approval` 事件。评估类需求不写代码,不经过确认点。
45
45
 
46
+ **原型审查页(1.13 起)。** 有 `prototype/` 的需求,插件的需求页面和"等你处理"里有"打开原型审查页",终端里是 `req review <需求>`。需求方在原型上点选要改的地方(页面记下元素、文字、颜色、大小和位置)、双击直接改文字,切换状态和电脑、手机宽度,然后提交:意见整理成带位置信息的清单转给需求会话(与插件中写修改意见的效果相同),或者直接确认方案。草稿和提交记录在功能目录的 `.pipeline/review/` 中,每一版原型也存在那里,原型改过后页面会标出这一版改了哪里。
47
+
48
+ 为了让审查页能用,方案设计按约定写原型:每个改动区域标 `data-ac`(对应的 AC)和 `data-label`(用需求方的话说是什么),各种状态包在 `<section data-state="状态名">` 中,按钮、标题、字段名写最终的文字。原型里的脚本不会运行。
49
+
50
+ 审查页由一个本机服务提供:用到时在后台启动,空闲 4 小时后自己退出,只监听 `127.0.0.1`,地址中带随机令牌(插件设为只读时给只读的地址)。需求方在别的电脑上用浏览器访问 Paseo 网页端时,在运行 Paseo daemon 的机器上设置环境变量 `REQ_KIT_REVIEW_HOST`(例如 `0.0.0.0`)、`REQ_KIT_REVIEW_PUBLIC_URL`(别人访问这台机器用的地址,例如 `http://10.1.2.3:6781`),需要时用 `REQ_KIT_REVIEW_PORT` 换端口(默认 6781),然后重启 Paseo daemon 让插件拿到新的环境变量。
51
+
46
52
  ## 代码评审分两个方向
47
53
 
48
54
  标准档第一轮代码评审由两位评审者同时进行(1.8 起),各自写意见,再由 reviewer 核实、汇总成一份结论:
@@ -256,7 +262,7 @@ spec 头部的 `- 类型:` 决定流水线走哪种做法(1.8 起)。缺
256
262
  | --- | --- | --- |
257
263
  | `specReview` | `"standard"` | 请需求方确认之前,由 reviewer 在后台独立审阅需求说明草稿,问题写进 `spec-review.md`。`"always"` 快速档也做,`"off"` 关闭 |
258
264
  | `designProposals` | 关闭 | `{"enabled": true}` 后,标准档的方案设计之前由几个 agent 并行各提一份方案(`design-proposals/`),方案设计比较后决定。`members` 不写时用 builder 和 reviewer,也可以像评审小组一样列出成员(可用 `paseoProfile`)。需求变更后的增量继续不做 |
259
- | `browserVerify` | `"auto"` | 步骤 agent 有 Paseo 浏览器工具(Paseo 设置中打开 Enable Paseo tools 和浏览器工具,桌面端在线)时,功能验证、缺陷修复、复验按验收标准在浏览器中实际操作。`"off"` 关闭 |
265
+ | `browserVerify` | `"auto"` | 功能验证、缺陷修复、复验检查网页界面的方式。1.13 起总是附上界面检查命令 `req ui-check`:用本机的 Edge 或 Chrome(无界面)打开页面,按电脑和手机宽度用文字报告显示不全、遮挡、超出屏幕、图片没加载、文字太淡、控件没有名字、控制台报错和请求失败,并和确认过的原型逐项对文字;不需要模型看图,也不需要 Paseo 桌面端。第一次使用时从 npm 源安装 `playwright-core` 到 `~/.req-kit/tools`(不下载浏览器)。步骤 agent 还有 Paseo 浏览器工具(桌面端在线)时,另外按验收标准在浏览器中实际操作。`"off"` 都不用 |
260
266
 
261
267
  Paseo 自带的 `/paseo-advisor`、`/paseo-committee` 等编排 skill 也可以单独使用,例如对一个疑难问题请两个模型分别分析;它们不参与流水线(见 ADR 0019)。
262
268
 
@@ -45,7 +45,11 @@
45
45
  - **判断是不是界面改动**:这次改动会不会改变使用者在页面、窗口、命令行输出、导出文件中看到的样子或操作方式。结果文件中写 `ui_change`(`true` / `false`)。
46
46
  - `<pipeline-context>` 写着"开启",或者写着"界面改动时开启"且 `ui_change` 为 `true` 时,在功能目录写 `preview.md`:
47
47
  1. **做完后你会看到什么**:按使用者的操作顺序描述(打开哪个页面、看到什么、点了之后发生什么),每一段注明对应的 AC。
48
- 2. **界面原型**(界面改动):在功能目录的 `prototype/` 下写一个或几个**静态 HTML 页面**(只用内联的 HTML 和 CSS,不引用外部文件,不连接任何服务),按项目现有页面的布局、文字和样式画出改动后的样子;正常、出错、空数据等关键状态各画一份或在同一页中并列。`preview.md` 中列出每个原型文件对应的 AC 和状态。原型只用来确认,不是产品代码,实现时不要复用它的代码。
48
+ 2. **界面原型**(界面改动):在功能目录的 `prototype/` 下写一个或几个**静态 HTML 页面**(只用内联的 HTML 和 CSS,不引用外部文件,不连接任何服务,不写脚本——审查页不会运行原型里的脚本),按项目现有页面的布局、文字和样式画出改动后的样子。`preview.md` 中列出每个原型文件对应的 AC 和状态。原型只用来确认,不是产品代码,实现时不要复用它的代码。需求方会在"原型审查页"上看原型、点选要改的地方,为了让审查页能用,按下面的写法:
49
+ - **标出改动区域**:这次新增或改变的每一块,在最外层元素上写 `data-ac="AC-1.2"`(对应的 AC,几条用空格分隔)和 `data-label="列表为空时的提示"`(用需求方的话说这是什么,十个字左右)。没改的部分不标,标得少才看得清。
50
+ - **状态**:正常、出错、空数据、无权限等关键状态放在同一页里,每个状态包在一个 `<section data-state="空数据">` 中(状态名用需求方的话),审查页会一次显示一个;状态之间相同的部分(页头、菜单)放在这些 section 外面。
51
+ - **文字用真的**:按钮、标题、字段名、提示语写成最终要上线的文字,不要写"按钮1""示例文字"。验证时会拿实际页面和原型逐项对文字。
52
+ - 页面的 `<title>` 写成需求方认得的页面名(如"订单列表"),审查页用它做标签。
49
53
  3. **接口示例**(改动接口时):一两组真实的请求和响应。
50
54
  4. **需要你确认的假设**:上文"假设"中影响界面、数据、权限的那些,用需求方的话写出来。
51
55
  5. **不会变的**:使用者可能担心、但这次不改的地方。
@@ -18,7 +18,7 @@
18
18
  | 意见编号 | 级别 | 处理结果(已修改 / 部分采纳 / 未采纳) | 说明,以及改动了哪些章节 |
19
19
  | --- | --- | --- | --- |
20
20
 
21
- 4. 有 `preview.md` 或 `prototype/` 时,方案改动涉及的部分同步修改,保持与 `design.md` 一致;`ui_change` 的判断变了的,在结果文件中写上新的值。
21
+ 4. 有 `preview.md` 或 `prototype/` 时,方案改动涉及的部分同步修改,保持与 `design.md` 一致(原型继续按方案设计中的写法标 `data-ac`、`data-label`、`data-state`,需求方会在原型审查页上对比这一版改了哪里);`ui_change` 的判断变了的,在结果文件中写上新的值。
22
22
  5. 审核中提出的"需要需求方决定的问题",原样保留在方案的"阻塞问题"或"假设"一节中,写明你暂时采用的处理方式。最终报告会把它们交给需求方。
23
23
 
24
24
  ## 完成标准
@@ -6,8 +6,8 @@
6
6
 
7
7
  1. 用 `browser_new_tab` 打开服务地址(服务由 Paseo 管理时用给出的地址)。
8
8
  2. 按验收标准的"操作"一步步做:`browser_snapshot` 找到元素,用 `browser_click`、`browser_fill`、`browser_select` 等操作,用 `browser_wait` 等待结果出现。
9
- 3. 用 `browser_snapshot` 核对页面上的结果是否符合"预期结果";关键画面用 `browser_screenshot` 截图。需要检查手机布局时用 `browser_resize`。出现异常时用 `browser_logs` 查看控制台和网络请求。
10
- 4. 在产物中记录:打开的地址、操作步骤、看到的结果、对应的 AC 编号,并注明"浏览器验证,截图见该步骤 agent 的对话记录"。
9
+ 3. 用 `browser_snapshot`(文字形式的页面结构)核对页面上的结果是否符合"预期结果"。版面问题用上面的界面检查命令;在这个浏览器里也可以检查:用 `browser_evaluate` 运行 `界面检查命令 --script` 输出的那段函数,它返回文字形式的问题清单。只在需要给需求方看某个画面时才用 `browser_screenshot`:截图会整张发给你,很费 token,看图判断也不如文字可靠。需要检查手机布局时用 `browser_resize`。出现异常时用 `browser_logs` 查看控制台和网络请求。
10
+ 4. 在产物中记录:打开的地址、操作步骤、看到的结果、对应的 AC 编号,并注明"浏览器验证"(截了图的,说明截图在该步骤 agent 的对话记录中)。
11
11
  5. 用完关闭你打开的标签页(`browser_close_tab`)。
12
12
 
13
13
  浏览器工具返回 `browser_no_host`(桌面端没有连接)或 `browser_disabled` 时,不要反复重试,改用接口调用、端到端测试等其他方式验证,并在产物中说明浏览器验证没能进行。不要在浏览器中登录生产环境或输入真实的个人信息。
@@ -0,0 +1,15 @@
1
+ # 不靠看图检查网页界面
2
+
3
+ `<pipeline-context>` 中有"界面检查命令"一行时,涉及网页界面的验收标准,除了按操作步骤核对结果,还要用这个命令检查页面**看起来**有没有问题。它用本机的 Edge 或 Chrome 打开页面,按电脑和手机两种宽度,用文字报告:文字显示不全、互相遮挡、超出屏幕、图片没加载、文字太淡、输入框和按钮没有名字、控制台报错、请求失败;给了原型时,还会拿原型里的标题、按钮、字段名、表头和页面逐项对文字。不需要看截图。
4
+
5
+ 1. 每个涉及界面的页面或状态运行一次,例如:
6
+ ```
7
+ <界面检查命令> http://127.0.0.1:3000/orders --prototype <功能目录>/prototype/list.html --state 正常 --shots <功能目录>/.pipeline/ui
8
+ ```
9
+ - 要先操作才能看到的状态(查询结果、空数据、出错),把操作写进一个 JSON 文件用 `--steps` 传入,例如 `[{"fill":["#keyword","不存在的订单"]},{"click":"text=查询"},{"wait":"text=暂无订单"}]`(选择器是 Playwright 的写法:`text=…`、`#id`、`role=button[name="查询"]`)。
10
+ - 原型中用 `data-state` 画了几种状态的,`--state` 写对应的状态名,每种状态各检查一次。
11
+ - 需要登录的页面:用项目的测试账号和测试环境,把登录操作写进 `--steps`;没有测试账号时,在产物中说明哪些页面没能检查。
12
+ 2. 逐条判断报告的内容。有的是故意的设计(例如故意截断的长文本带有完整提示),写明理由后不算缺陷;其余按缺陷处理,在 `verification.md` 中记下报告原文的那一行和页面地址。"原型里有、页面上没有"的文字,先确认是不是需求方确认过要有的内容。
13
+ 3. 截图保存在 `--shots` 目录,是给人看的:**不要再把截图读进对话**,它对判断帮助不大,还很费 token。
14
+
15
+ 命令打不开浏览器(这台机器没有 Edge 或 Chrome,或者装驱动失败)时,不要反复重试,在产物中写明"界面检查没能进行"和命令的输出,改用接口调用、项目自带的端到端测试等方式验证。
package/engine/src/api.ts CHANGED
@@ -99,6 +99,8 @@ function currentSteps(state: PipelineState | null) {
99
99
  }
100
100
 
101
101
  export interface ListItem {
102
+ /** 1.13: the requirement has an interface prototype (the review page can show it). */
103
+ hasPrototype?: boolean;
102
104
  key: string;
103
105
  project: string;
104
106
  repo: string;
@@ -163,6 +165,7 @@ function item(repo: string, branch: string | null, featureDir: string, extra: {
163
165
  usage: usageOf(state),
164
166
  present: true,
165
167
  waiting: waitingOf(repo, featureDir, state),
168
+ hasPrototype: fs.existsSync(path.join(repo, featureDir, "prototype")),
166
169
  };
167
170
  }
168
171
 
@@ -447,8 +450,20 @@ export function apiMain(argvIn: string[], cwd = process.cwd()): unknown {
447
450
  const { message } = configureJson(["--set", value]);
448
451
  return { ok: true, message };
449
452
  }
453
+ case "review-url": {
454
+ // 1.13: the prototype review page of a requirement (starts the page's server when needed).
455
+ const feature = need("需求");
456
+ const readOnly = flag(argv, "--read-only");
457
+ const { repo: r, featureDir } = locate(repo, feature);
458
+ return import("./review.js").then(async ({ ensureReviewServer, keyOf, reviewUrl }) => {
459
+ const key = keyOf(r, featureDir);
460
+ if (!key) throw new ApiError("not_found", `${featureDir} 不在本机的需求列表中`);
461
+ if (!fs.existsSync(path.join(r, featureDir, "prototype"))) throw new ApiError("not_found", `${featureDir} 还没有界面原型`);
462
+ return { url: reviewUrl(await ensureReviewServer(), key, readOnly), readOnly };
463
+ });
464
+ }
450
465
  default:
451
- throw new ApiError("bad_arguments", `没有 "${command ?? ""}" 这个 api 命令。可用:version、list、status、file、stats、index、run、models、set-agents、config`);
466
+ throw new ApiError("bad_arguments", `没有 "${command ?? ""}" 这个 api 命令。可用:version、list、status、file、stats、index、run、models、set-agents、config、review-url`);
452
467
  }
453
468
  }
454
469
 
@@ -456,7 +471,7 @@ if (process.argv[1] && path.resolve(process.argv[1]) === path.resolve(import.met
456
471
  let code = 0;
457
472
  let out: Record<string, unknown>;
458
473
  try {
459
- out = { schema: API_SCHEMA, kitVersion: kitVersion(), data: apiMain(process.argv.slice(2)) };
474
+ out = { schema: API_SCHEMA, kitVersion: kitVersion(), data: await apiMain(process.argv.slice(2)) };
460
475
  const data = out.data as { ok?: boolean } | null;
461
476
  if (data && data.ok === false) code = 1;
462
477
  } catch (error) {
@@ -114,7 +114,7 @@ const CATALOG: Array<{ id: string; title: string; description: string; keys: Rec
114
114
  specReview: { label: "需求说明审阅", description: "开发前由另一个模型审一遍需求说明。", display: (v) => ({ off: "关", standard: "标准档", always: "所有档位" })[String(v)] ?? String(v) },
115
115
  requirementTypes: { label: "按类型的做法", description: "缺陷、重构、评估各走自己的做法。", display: (v) => list((v as string[]).map((t) => ({ defect: "缺陷", refactor: "重构", assessment: "评估" })[t] ?? t)) },
116
116
  slices: { label: "按切片实现", description: "方案分几片时逐片实现。", display: (v) => { const s = v as PipelineConfig["slices"]; return `${profilesText(s.profiles)},最多 ${s.max} 片`; } },
117
- browserVerify: { label: "用浏览器验证", description: "Paseo 有内置浏览器时,验证网页界面。", display: (v) => ({ auto: "自动", off: "关" })[String(v)] ?? String(v) },
117
+ browserVerify: { label: "检查网页界面", description: "功能验证时用界面检查命令(不靠看图)检查网页;Paseo 有内置浏览器时也用它操作。", display: (v) => ({ auto: "自动", off: "关" })[String(v)] ?? String(v) },
118
118
  paseoServices: { label: "由 Paseo 启动服务", description: "用 paseo.json 中的服务脚本启动和重启服务。", display: (v) => ({ auto: "自动", off: "关" })[String(v)] ?? String(v) },
119
119
  serviceReadySeconds: { label: "等服务就绪", description: "启动服务后最多等多久。", display: (v) => `${v} 秒` },
120
120
  },
@@ -30,7 +30,7 @@ import {
30
30
  permissionMessage,
31
31
  resultFollowUp,
32
32
  } from "./messages.js";
33
- import { kitVersion } from "./locations.js";
33
+ import { KIT_ROOT, kitVersion } from "./locations.js";
34
34
  import { type AgentUsage, describeTotals, totals } from "./usage.js";
35
35
  import { dispatchNotifiers, type NotifierEventType, peopleOf } from "./notifiers.js";
36
36
  import { describeExternal, type IntegrationEvent, renderArgs, resolveCommand, type Runner, syncIntegration } from "./integrations.js";
@@ -964,15 +964,23 @@ function mergeExtras(
964
964
  /** Steps that check the result the way a user would. */
965
965
  const BROWSER_STEPS: readonly StepId[] = ["verify", "defect_fix", "reverify"];
966
966
 
967
- /** Points verification at Paseo's built-in browser when the step agents have it. */
967
+ /**
968
+ * How verification looks at web pages: `req ui-check` (1.13; reports in words, needs no vision
969
+ * and no Paseo desktop) always, plus Paseo's built-in browser when the step agents have it.
970
+ */
968
971
  async function browserExtras(ctx: PipelineContext, id: StepId): Promise<{ contextLines: string[]; sections: string[] } | null> {
969
972
  if (!BROWSER_STEPS.includes(id) || ctx.config.browserVerify === "off") return null;
970
- if (ctx.capabilities === undefined) ctx.capabilities = await ctx.runtime.capabilities().catch(() => null);
971
- if (!ctx.capabilities?.browser) return null;
972
- return {
973
- contextLines: ["浏览器:可以用 Paseo 内置浏览器(browser_* 工具)验证网页界面"],
974
- sections: [readToolPrompt({ ...ctx.prompts, repo: ctx.repo }, "browser-verify.md")],
973
+ const prompts = { ...ctx.prompts, repo: ctx.repo };
974
+ const out = {
975
+ contextLines: [`界面检查命令:node "${path.join(KIT_ROOT, "bin", "req.mjs")}" ui-check`],
976
+ sections: [readToolPrompt(prompts, "ui-check.md")],
975
977
  };
978
+ if (ctx.capabilities === undefined) ctx.capabilities = await ctx.runtime.capabilities().catch(() => null);
979
+ if (ctx.capabilities?.browser) {
980
+ out.contextLines.push("浏览器:可以用 Paseo 内置浏览器(browser_* 工具)验证网页界面");
981
+ out.sections.push(readToolPrompt(prompts, "browser-verify.md"));
982
+ }
983
+ return out;
976
984
  }
977
985
 
978
986
  /** Steps that need the project's services running. */