paseo-req-kit 1.12.0 → 1.13.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/README.md +8 -3
- package/bin/bootstrap.d.mts +12 -0
- package/bin/bootstrap.mjs +418 -0
- package/bin/req-docs.d.mts +5 -0
- package/bin/req-docs.mjs +169 -0
- package/bin/req.mjs +12 -1
- package/docs/adr/0032-npm-only-install.md +27 -0
- package/docs/adr/README.md +1 -0
- package/docs/guide//345/256/211/350/243/205/344/270/216/345/215/207/347/272/247.md +22 -2
- package/docs/guide//347/273/264/346/212/244/350/200/205/346/214/207/345/215/227.md +1 -1
- 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 +2 -2
- package/engine/src/card-cmd.ts +18 -3
- package/engine/src/card.ts +26 -3
- package/engine/src/daemon-check.ts +2 -1
- package/engine/src/messages.ts +9 -1
- package/engine/src/paseo-runtime.ts +27 -2
- package/engine/src/pipeline.ts +33 -8
- package/engine/src/state.ts +20 -1
- package/package.json +3 -1
- package/paseo-plugin/package.json +1 -1
- package/site-dist/404.html +27 -0
- package/site-dist/assets/app.C_2Vhmv-.js +1 -0
- package/site-dist/assets/changelog.md.VHiXRXap.js +1 -0
- package/site-dist/assets/changelog.md.VHiXRXap.lean.js +1 -0
- package/site-dist/assets/chunks/@localSearchIndexroot.hC_y1UNZ.js +1 -0
- package/site-dist/assets/chunks/VPLocalSearchBox.zPMXlb4V.js +9 -0
- package/site-dist/assets/chunks/framework.B9qR_F1m.js +20 -0
- package/site-dist/assets/chunks/theme.DRjHgCIR.js +2 -0
- package/site-dist/assets/guide_getting-started.md.DjqrOQ_8.js +6 -0
- package/site-dist/assets/guide_getting-started.md.DjqrOQ_8.lean.js +1 -0
- package/site-dist/assets/guide_how-it-works.md.D-kQsF1t.js +25 -0
- package/site-dist/assets/guide_how-it-works.md.D-kQsF1t.lean.js +1 -0
- package/site-dist/assets/guide_index.md.CLnGCmGW.js +1 -0
- package/site-dist/assets/guide_index.md.CLnGCmGW.lean.js +1 -0
- package/site-dist/assets/guide_install.md.DkCyZsOU.js +42 -0
- package/site-dist/assets/guide_install.md.DkCyZsOU.lean.js +42 -0
- package/site-dist/assets/guide_maintainer.md.C7Aav_lk.js +1 -0
- package/site-dist/assets/guide_maintainer.md.C7Aav_lk.lean.js +1 -0
- package/site-dist/assets/guide_phases.md.BpGAb1re.js +1 -0
- package/site-dist/assets/guide_phases.md.BpGAb1re.lean.js +1 -0
- package/site-dist/assets/guide_project.md.r2g5uU_4.js +77 -0
- package/site-dist/assets/guide_project.md.r2g5uU_4.lean.js +77 -0
- package/site-dist/assets/guide_user.md.FpCmz9S0.js +1 -0
- package/site-dist/assets/guide_user.md.FpCmz9S0.lean.js +1 -0
- package/site-dist/assets/index.md.Cf_sSf_f.js +1 -0
- package/site-dist/assets/index.md.Cf_sSf_f.lean.js +1 -0
- package/site-dist/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
- package/site-dist/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
- package/site-dist/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
- package/site-dist/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
- package/site-dist/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
- package/site-dist/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
- package/site-dist/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
- package/site-dist/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
- package/site-dist/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
- package/site-dist/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
- package/site-dist/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
- package/site-dist/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
- package/site-dist/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
- package/site-dist/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
- package/site-dist/assets/reference_adr_0001-step-agents-under-origin-chat.md.RtRTq7oi.js +1 -0
- package/site-dist/assets/reference_adr_0001-step-agents-under-origin-chat.md.RtRTq7oi.lean.js +1 -0
- package/site-dist/assets/reference_adr_0002-no-directory-switch-in-chat.md.BxtN-Aa2.js +1 -0
- package/site-dist/assets/reference_adr_0002-no-directory-switch-in-chat.md.BxtN-Aa2.lean.js +1 -0
- package/site-dist/assets/reference_adr_0003-script-engine-not-hub.md.66EgZtqx.js +1 -0
- package/site-dist/assets/reference_adr_0003-script-engine-not-hub.md.66EgZtqx.lean.js +1 -0
- package/site-dist/assets/reference_adr_0004-result-files.md.DbqcCozI.js +1 -0
- package/site-dist/assets/reference_adr_0004-result-files.md.DbqcCozI.lean.js +1 -0
- package/site-dist/assets/reference_adr_0005-no-default-branch-local-commits-only.md.CkImA4s6.js +1 -0
- package/site-dist/assets/reference_adr_0005-no-default-branch-local-commits-only.md.CkImA4s6.lean.js +1 -0
- package/site-dist/assets/reference_adr_0006-skill-install-targets.md.CwY8YepV.js +1 -0
- package/site-dist/assets/reference_adr_0006-skill-install-targets.md.CwY8YepV.lean.js +1 -0
- package/site-dist/assets/reference_adr_0007-own-interview-skill.md.CryhxRyG.js +1 -0
- package/site-dist/assets/reference_adr_0007-own-interview-skill.md.CryhxRyG.lean.js +1 -0
- package/site-dist/assets/reference_adr_0008-entries-do-not-call-entries.md.DsRWAgeA.js +1 -0
- package/site-dist/assets/reference_adr_0008-entries-do-not-call-entries.md.DsRWAgeA.lean.js +1 -0
- package/site-dist/assets/reference_adr_0009-versioned-install-layout.md.CSg1VqlW.js +1 -0
- package/site-dist/assets/reference_adr_0009-versioned-install-layout.md.CSg1VqlW.lean.js +1 -0
- package/site-dist/assets/reference_adr_0010-generated-feature-overview.md.D8F3MK-h.js +1 -0
- package/site-dist/assets/reference_adr_0010-generated-feature-overview.md.D8F3MK-h.lean.js +1 -0
- package/site-dist/assets/reference_adr_0011-resume-after-completion.md.BCrZrKjf.js +1 -0
- package/site-dist/assets/reference_adr_0011-resume-after-completion.md.BCrZrKjf.lean.js +1 -0
- package/site-dist/assets/reference_adr_0012-review-panel.md.k9rrHFFc.js +1 -0
- package/site-dist/assets/reference_adr_0012-review-panel.md.k9rrHFFc.lean.js +1 -0
- package/site-dist/assets/reference_adr_0013-no-release-tooling.md.BQeJ8dEI.js +1 -0
- package/site-dist/assets/reference_adr_0013-no-release-tooling.md.BQeJ8dEI.lean.js +1 -0
- package/site-dist/assets/reference_adr_0014-notifier-slot.md.C23QR1xO.js +1 -0
- package/site-dist/assets/reference_adr_0014-notifier-slot.md.C23QR1xO.lean.js +1 -0
- package/site-dist/assets/reference_adr_0015-incremental-continue.md.Buy7wwO8.js +1 -0
- package/site-dist/assets/reference_adr_0015-incremental-continue.md.Buy7wwO8.lean.js +1 -0
- package/site-dist/assets/reference_adr_0016-upgrade-from-tags.md.qfIW-tqJ.js +1 -0
- package/site-dist/assets/reference_adr_0016-upgrade-from-tags.md.qfIW-tqJ.lean.js +1 -0
- package/site-dist/assets/reference_adr_0017-use-paseo-capabilities.md.CjcGli87.js +1 -0
- package/site-dist/assets/reference_adr_0017-use-paseo-capabilities.md.CjcGli87.lean.js +1 -0
- package/site-dist/assets/reference_adr_0018-own-worktree-option.md.fVDpgGSE.js +1 -0
- package/site-dist/assets/reference_adr_0018-own-worktree-option.md.fVDpgGSE.lean.js +1 -0
- package/site-dist/assets/reference_adr_0019-parallel-orchestration.md.DrfrV0To.js +1 -0
- package/site-dist/assets/reference_adr_0019-parallel-orchestration.md.DrfrV0To.lean.js +1 -0
- package/site-dist/assets/reference_adr_0020-scheduling-through-the-chat.md.BqSemGFG.js +1 -0
- package/site-dist/assets/reference_adr_0020-scheduling-through-the-chat.md.BqSemGFG.lean.js +1 -0
- package/site-dist/assets/reference_adr_0021-npm-version-manager.md.CU3PG6y5.js +1 -0
- package/site-dist/assets/reference_adr_0021-npm-version-manager.md.CU3PG6y5.lean.js +1 -0
- package/site-dist/assets/reference_adr_0022-develop-and-verify-phases.md.C6fQDLq4.js +1 -0
- package/site-dist/assets/reference_adr_0022-develop-and-verify-phases.md.C6fQDLq4.lean.js +1 -0
- package/site-dist/assets/reference_adr_0023-types-gate-slices.md.D2E6yyYQ.js +1 -0
- package/site-dist/assets/reference_adr_0023-types-gate-slices.md.D2E6yyYQ.lean.js +1 -0
- package/site-dist/assets/reference_adr_0024-confirm-early-review-axes.md.FmK_ke0x.js +1 -0
- package/site-dist/assets/reference_adr_0024-confirm-early-review-axes.md.FmK_ke0x.lean.js +1 -0
- package/site-dist/assets/reference_adr_0025-joint-requirements.md.ChoKvMUx.js +1 -0
- package/site-dist/assets/reference_adr_0025-joint-requirements.md.ChoKvMUx.lean.js +1 -0
- package/site-dist/assets/reference_adr_0026-machine-records-and-api.md.veybjBPq.js +1 -0
- package/site-dist/assets/reference_adr_0026-machine-records-and-api.md.veybjBPq.lean.js +1 -0
- package/site-dist/assets/reference_adr_0027-paseo-plugin.md.BMX33ZNU.js +1 -0
- package/site-dist/assets/reference_adr_0027-paseo-plugin.md.BMX33ZNU.lean.js +1 -0
- package/site-dist/assets/reference_adr_0028-external-integrations.md.DtdMe8RK.js +1 -0
- package/site-dist/assets/reference_adr_0028-external-integrations.md.DtdMe8RK.lean.js +1 -0
- package/site-dist/assets/reference_adr_0029-requirements-from-cards.md.BHd1SQPe.js +1 -0
- package/site-dist/assets/reference_adr_0029-requirements-from-cards.md.BHd1SQPe.lean.js +1 -0
- package/site-dist/assets/reference_adr_0030-card-intake-process.md.kLc-DG9_.js +1 -0
- package/site-dist/assets/reference_adr_0030-card-intake-process.md.kLc-DG9_.lean.js +1 -0
- package/site-dist/assets/reference_adr_0031-fewer-interventions.md.BDnQcz8g.js +1 -0
- package/site-dist/assets/reference_adr_0031-fewer-interventions.md.BDnQcz8g.lean.js +1 -0
- package/site-dist/assets/reference_adr_0032-npm-only-install.md.C-3L1FkX.js +1 -0
- package/site-dist/assets/reference_adr_0032-npm-only-install.md.C-3L1FkX.lean.js +1 -0
- package/site-dist/assets/reference_adr_index.md.DTVyIA6V.js +1 -0
- package/site-dist/assets/reference_adr_index.md.DTVyIA6V.lean.js +1 -0
- package/site-dist/assets/reference_commands.md.ClKbS-t8.js +1 -0
- package/site-dist/assets/reference_commands.md.ClKbS-t8.lean.js +1 -0
- package/site-dist/assets/reference_config.md.CRe27LaA.js +6 -0
- package/site-dist/assets/reference_config.md.CRe27LaA.lean.js +1 -0
- package/site-dist/assets/reference_contracts_cli-json.md.gkER6rX7.js +1 -0
- package/site-dist/assets/reference_contracts_cli-json.md.gkER6rX7.lean.js +1 -0
- package/site-dist/assets/reference_contracts_feature-files.md.DSiA8OEH.js +26 -0
- package/site-dist/assets/reference_contracts_feature-files.md.DSiA8OEH.lean.js +26 -0
- package/site-dist/assets/reference_contracts_index.md.Bkznz4px.js +1 -0
- package/site-dist/assets/reference_contracts_index.md.Bkznz4px.lean.js +1 -0
- package/site-dist/assets/reference_contracts_notifier-event.md.CCfrkm_A.js +24 -0
- package/site-dist/assets/reference_contracts_notifier-event.md.CCfrkm_A.lean.js +24 -0
- package/site-dist/assets/reference_contracts_result-file.md.DmR4fjB-.js +1 -0
- package/site-dist/assets/reference_contracts_result-file.md.DmR4fjB-.lean.js +1 -0
- package/site-dist/assets/reference_contracts_spec.md.BwwS0L_j.js +8 -0
- package/site-dist/assets/reference_contracts_spec.md.BwwS0L_j.lean.js +8 -0
- package/site-dist/assets/reference_contracts_state.md.5bNTC3ow.js +1 -0
- package/site-dist/assets/reference_contracts_state.md.5bNTC3ow.lean.js +1 -0
- package/site-dist/assets/reference_contracts_usage-record.md._C0OXWeq.js +10 -0
- package/site-dist/assets/reference_contracts_usage-record.md._C0OXWeq.lean.js +10 -0
- package/site-dist/assets/reference_contracts_verify.md.CbA88u6S.js +29 -0
- package/site-dist/assets/reference_contracts_verify.md.CbA88u6S.lean.js +29 -0
- package/site-dist/assets/reference_scope.md.Kzqw8U_f.js +1 -0
- package/site-dist/assets/reference_scope.md.Kzqw8U_f.lean.js +1 -0
- package/site-dist/assets/style.Dwsa4SMe.css +1 -0
- package/site-dist/changelog.html +30 -0
- package/site-dist/guide/getting-started.html +35 -0
- package/site-dist/guide/how-it-works.html +54 -0
- package/site-dist/guide/index.html +30 -0
- package/site-dist/guide/install.html +71 -0
- package/site-dist/guide/maintainer.html +30 -0
- package/site-dist/guide/phases.html +30 -0
- package/site-dist/guide/project.html +106 -0
- package/site-dist/guide/user.html +30 -0
- package/site-dist/hashmap.json +1 -0
- package/site-dist/icons/agents.svg +1 -0
- package/site-dist/icons/chat.svg +1 -0
- package/site-dist/icons/paseo.svg +1 -0
- package/site-dist/icons/phases.svg +1 -0
- package/site-dist/icons/review.svg +1 -0
- package/site-dist/icons/safe.svg +1 -0
- package/site-dist/index.html +30 -0
- package/site-dist/install.ps1 +118 -0
- package/site-dist/install.sh +198 -0
- package/site-dist/logo.svg +11 -0
- package/site-dist/reference/adr/0001-step-agents-under-origin-chat.html +30 -0
- package/site-dist/reference/adr/0002-no-directory-switch-in-chat.html +30 -0
- package/site-dist/reference/adr/0003-script-engine-not-hub.html +30 -0
- package/site-dist/reference/adr/0004-result-files.html +30 -0
- package/site-dist/reference/adr/0005-no-default-branch-local-commits-only.html +30 -0
- package/site-dist/reference/adr/0006-skill-install-targets.html +30 -0
- package/site-dist/reference/adr/0007-own-interview-skill.html +30 -0
- package/site-dist/reference/adr/0008-entries-do-not-call-entries.html +30 -0
- package/site-dist/reference/adr/0009-versioned-install-layout.html +30 -0
- package/site-dist/reference/adr/0010-generated-feature-overview.html +30 -0
- package/site-dist/reference/adr/0011-resume-after-completion.html +30 -0
- package/site-dist/reference/adr/0012-review-panel.html +30 -0
- package/site-dist/reference/adr/0013-no-release-tooling.html +30 -0
- package/site-dist/reference/adr/0014-notifier-slot.html +30 -0
- package/site-dist/reference/adr/0015-incremental-continue.html +30 -0
- package/site-dist/reference/adr/0016-upgrade-from-tags.html +30 -0
- package/site-dist/reference/adr/0017-use-paseo-capabilities.html +30 -0
- package/site-dist/reference/adr/0018-own-worktree-option.html +30 -0
- package/site-dist/reference/adr/0019-parallel-orchestration.html +30 -0
- package/site-dist/reference/adr/0020-scheduling-through-the-chat.html +30 -0
- package/site-dist/reference/adr/0021-npm-version-manager.html +30 -0
- package/site-dist/reference/adr/0022-develop-and-verify-phases.html +30 -0
- package/site-dist/reference/adr/0023-types-gate-slices.html +30 -0
- package/site-dist/reference/adr/0024-confirm-early-review-axes.html +30 -0
- package/site-dist/reference/adr/0025-joint-requirements.html +30 -0
- package/site-dist/reference/adr/0026-machine-records-and-api.html +30 -0
- package/site-dist/reference/adr/0027-paseo-plugin.html +30 -0
- package/site-dist/reference/adr/0028-external-integrations.html +30 -0
- package/site-dist/reference/adr/0029-requirements-from-cards.html +30 -0
- package/site-dist/reference/adr/0030-card-intake-process.html +30 -0
- package/site-dist/reference/adr/0031-fewer-interventions.html +30 -0
- package/site-dist/reference/adr/0032-npm-only-install.html +30 -0
- package/site-dist/reference/adr/index.html +30 -0
- package/site-dist/reference/commands.html +30 -0
- package/site-dist/reference/config.html +35 -0
- package/site-dist/reference/contracts/cli-json.html +30 -0
- package/site-dist/reference/contracts/feature-files.html +55 -0
- package/site-dist/reference/contracts/index.html +30 -0
- package/site-dist/reference/contracts/notifier-event.html +53 -0
- package/site-dist/reference/contracts/result-file.html +30 -0
- package/site-dist/reference/contracts/spec.html +37 -0
- package/site-dist/reference/contracts/state.html +30 -0
- package/site-dist/reference/contracts/usage-record.html +39 -0
- package/site-dist/reference/contracts/verify.html +58 -0
- package/site-dist/reference/scope.html +30 -0
- package/site-dist/vp-icons.css +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
每个版本分为"使用者会看到的变化"和"升级须知"。升级步骤见 [docs/guide/安装与升级.md](docs/guide/安装与升级.md)。
|
|
4
4
|
|
|
5
|
+
## 1.13.0-beta.1
|
|
6
|
+
|
|
7
|
+
公司内网只有 npm 镜像、打不开 GitHub 和文档站点时,也能一条命令装好、升级和看文档。见 [ADR 0032](docs/adr/0032-npm-only-install.md)。
|
|
8
|
+
|
|
9
|
+
### 使用者会看到的变化
|
|
10
|
+
|
|
11
|
+
- **npx 一条命令安装**:`npx -y paseo-req-kit@latest install`,任何系统都可以,只需要 Node.js 22+ 和能访问的 npm 源。做的事和以前的一键安装完全一样:检查环境、安装、检查和安装 Paseo、启用、自动选好 agent、询问是否装插件。已安装时也可以运行 `req install`。
|
|
12
|
+
- **公司内网**:`npx -y --registry=<镜像地址> paseo-req-kit@latest install`。镜像地址会保存为本机的 npm 源(屏幕上写出原来的值),以后 `req upgrade` 和安装 Paseo 命令行也走镜像;只想用一次加 `--no-save-registry`。镜像还没同步这个版本时直接说明,不再去访问 GitHub。
|
|
13
|
+
- **`req docs`**:在本机打开和所装版本一致的文档站点,不需要外网(发布时构建进 npm 包)。
|
|
14
|
+
- **安装脚本**:`install.sh` / `install.ps1` 用法不变,新增 `--registry` / `$ReqKitRegistry` 和 `--no-save-registry` / `$ReqKitNoSaveRegistry`。文档站点首页增加 npx 的写法;站点放在别的地址(例如内网)时,首页的安装命令自动换成那个地址。
|
|
15
|
+
|
|
16
|
+
### 升级须知
|
|
17
|
+
|
|
18
|
+
- 安装脚本现在只负责选版本,其余步骤由所选版本自带的 `req install` 完成,所以新脚本只能安装 1.13 及以后的版本。要装更早的版本:`npm i -g paseo-req-kit@<版本> && req setup`。
|
|
19
|
+
- npm 包增加了 `site-dist/`(构建好的文档站点,约几 MB)和命令 `paseo-req-kit`(与 `req` 相同)。
|
|
20
|
+
|
|
21
|
+
## 1.12.1
|
|
22
|
+
|
|
23
|
+
修正 1.12.0 审查中发现的问题,不改变用法。
|
|
24
|
+
|
|
25
|
+
### 使用者会看到的变化
|
|
26
|
+
|
|
27
|
+
- **需求改了以后不再沿用旧 agent**:继续时如果某一步还有上一次尝试留下的 agent(按旧需求在做),会先停掉它再新开,不再接着等它或采用它的结果。
|
|
28
|
+
- **超时只多等一轮**:接着等上一次的 agent 时不再重新延长,卡住的步骤最多等两倍时限就会换人。
|
|
29
|
+
- **自答的问题有人复核**:方案审核最后一轮的问题由 builder 自答后,再加一轮方案审核复核这些答案,然后才写代码(快速档也一样)。
|
|
30
|
+
- **连不上 Paseo 时不再挂住或泄漏连接**:所有连接 30 秒连不上就放弃并关闭;接单器不会在 Paseo 离线期间积攒后台重连的连接。
|
|
31
|
+
- **卡片回复更稳妥**:卡片上还有没回答的问题,或回复区只写了一个"是""对"时,不再当作确认;需求方写的内容里如果带有 `</card-reply>` 这样的标签,不会再被当成 req-kit 的话。
|
|
32
|
+
- **两个进程同时更新卡片记录时不再互相覆盖**(流水线写 AI 区与接单器转达回复)。
|
|
33
|
+
- **Windows**:状态文件被杀毒软件或索引服务短暂占用时会重试,不再让流水线意外退出。
|
|
34
|
+
- **发布流程**:测试通过后才打版本标签,失败的发布不再占用版本号。
|
|
35
|
+
|
|
5
36
|
## 1.12.0
|
|
6
37
|
|
|
7
38
|
稳定版:少等人,内容同 1.12.0-beta.1 至 beta.4(详见下面各条),设计决定见 ADR 0031。
|
package/README.md
CHANGED
|
@@ -56,7 +56,12 @@ AI ▸ 结论:已完成,可以验收。6 条验收标准全部通过……
|
|
|
56
56
|
|
|
57
57
|
## 快速开始
|
|
58
58
|
|
|
59
|
-
在要运行 Paseo 的机器上(需要 Node.js 22+;还没有 Paseo
|
|
59
|
+
在要运行 Paseo 的机器上(需要 Node.js 22+;还没有 Paseo 也可以,会一起装好):
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# 任何系统;公司内网只有 npm 镜像时加 --registry=<镜像地址>(npx -y --registry=… paseo-req-kit@latest install)
|
|
63
|
+
npx -y paseo-req-kit@latest install
|
|
64
|
+
```
|
|
60
65
|
|
|
61
66
|
```bash
|
|
62
67
|
# macOS、Linux、Windows 的 Git Bash 或 WSL
|
|
@@ -68,7 +73,7 @@ curl -fsSL https://paseo-req-kit.vercel.app/install.sh | sh
|
|
|
68
73
|
irm https://paseo-req-kit.vercel.app/install.ps1 | iex
|
|
69
74
|
```
|
|
70
75
|
|
|
71
|
-
安装脚本会检查环境、安装 `paseo-req-kit`;没有 Paseo 时安装 Paseo 命令行,开启它自带的网页界面(`http://localhost:6767/`)并启动 daemon;为 Claude Code / Codex / OpenCode / Pi 安装 `/req` 等 skill,并引导你为 builder 和 reviewer 选择 agent
|
|
76
|
+
安装脚本会检查环境、安装 `paseo-req-kit`;没有 Paseo 时安装 Paseo 命令行,开启它自带的网页界面(`http://localhost:6767/`)并启动 daemon;为 Claude Code / Codex / OpenCode / Pi 安装 `/req` 等 skill,并引导你为 builder 和 reviewer 选择 agent。再次运行即升级;`req docs` 在本机打开文档。然后:
|
|
72
77
|
|
|
73
78
|
1. `req doctor` —— 检查 daemon、agent 模式和额度;
|
|
74
79
|
2. 在项目的 Paseo 对话中运行 `/req-setup` —— 接入项目(每个项目一次);
|
|
@@ -115,7 +120,7 @@ Paseo 对话(/req) 流水线引擎(后台进
|
|
|
115
120
|
| `skills/` | 安装到各 agent 的 skill:入口(`/req`、`/req-setup`、`/req-doctor`、`/req-retro`)、方法(访谈、需求说明、验收标准、结果说明)、原则 |
|
|
116
121
|
| `engine/` | 流水线引擎:`src/` 程序、`prompts/` 步骤提示词、`test/` 场景测试(模拟 Paseo 运行时) |
|
|
117
122
|
| `bin/` | `req`、`start-pipeline`、`req-doctor` 命令 |
|
|
118
|
-
| `install.mjs`、`install.sh`、`install.ps1` |
|
|
123
|
+
| `install.mjs`、`bin/bootstrap.mjs`、`install.sh`、`install.ps1` | 安装、升级、回滚、卸载;一键安装(`req install`)和安装脚本 |
|
|
119
124
|
| `contracts/` | spec、状态文件、结果文件、通知事件等的格式约定 |
|
|
120
125
|
| `docs/` | 使用指南与设计决定(ADR) |
|
|
121
126
|
| `evals/` | 端到端评估:样例仓库、隐藏验收测试、对话剧本 |
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Types for the parts of bootstrap.mjs the tests use.
|
|
2
|
+
export function parseArgs(argv: string[]): {
|
|
3
|
+
registry: string | null;
|
|
4
|
+
saveRegistry: boolean;
|
|
5
|
+
configure: boolean;
|
|
6
|
+
paseo: boolean;
|
|
7
|
+
plugin: "ask" | "yes" | "no";
|
|
8
|
+
spec: string | null;
|
|
9
|
+
help: boolean;
|
|
10
|
+
};
|
|
11
|
+
export function sameRegistry(a: string | null | undefined, b: string | null | undefined): boolean;
|
|
12
|
+
export function which(name: string, env?: NodeJS.ProcessEnv | Record<string, string>, skipRoot?: string | null): string | null;
|
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// `req install` (1.13): the whole one-step install in Node.js, so a machine that can reach an
|
|
3
|
+
// npm registry (the public one or a company mirror) needs nothing else:
|
|
4
|
+
//
|
|
5
|
+
// npx -y paseo-req-kit@latest install
|
|
6
|
+
// npx -y --registry=https://npm.example.internal/ paseo-req-kit@latest install
|
|
7
|
+
//
|
|
8
|
+
// Steps: check Node.js, npm and git; use (and save) the npm registry; npm install -g this
|
|
9
|
+
// version; check Paseo (install the matching Paseo CLI when there is neither a paseo command
|
|
10
|
+
// nor a running daemon, turn on its web UI, start the daemon); req setup; req configure --auto;
|
|
11
|
+
// offer the Paseo plugin. Running it again upgrades in place. install.sh and install.ps1 only
|
|
12
|
+
// pick the version and run this.
|
|
13
|
+
//
|
|
14
|
+
// Tests: REQ_KIT_NPM (a script standing in for npm), REQ_KIT_PASEO_BIN (the paseo command; empty:
|
|
15
|
+
// none), REQ_KIT_PASEO_URL (the daemon), REQ_KIT_PASEO_WAIT (seconds to wait for it to start).
|
|
16
|
+
import { spawnSync } from "node:child_process";
|
|
17
|
+
import fs from "node:fs";
|
|
18
|
+
import os from "node:os";
|
|
19
|
+
import path from "node:path";
|
|
20
|
+
import { createInterface } from "node:readline/promises";
|
|
21
|
+
import { fileURLToPath } from "node:url";
|
|
22
|
+
|
|
23
|
+
const KIT_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
24
|
+
const PKG = JSON.parse(fs.readFileSync(path.join(KIT_ROOT, "package.json"), "utf8"));
|
|
25
|
+
const WINDOWS = process.platform === "win32";
|
|
26
|
+
const DEFAULT_REGISTRY = "https://registry.npmjs.org/";
|
|
27
|
+
|
|
28
|
+
const HELP = `用法:npx -y [--registry=<npm 源>] paseo-req-kit@latest install [选项]
|
|
29
|
+
(已安装时也可以运行 req install)
|
|
30
|
+
|
|
31
|
+
--registry <地址> 从这个 npm 源安装(例如公司的 npm 镜像),并保存为本机的 npm 源,
|
|
32
|
+
以后 req upgrade 也用它
|
|
33
|
+
--no-save-registry 只在这次安装中使用 --registry,不保存
|
|
34
|
+
--no-configure 不运行选择 agent 的配置向导
|
|
35
|
+
--skip-paseo 不检查、不安装、不启动 Paseo
|
|
36
|
+
--with-plugin 不询问,直接安装 Paseo 的 req-kit 插件(会打开 Paseo 的插件总开关)
|
|
37
|
+
--no-plugin 不安装、也不询问 Paseo 插件
|
|
38
|
+
-h, --help 显示这段说明
|
|
39
|
+
|
|
40
|
+
安装 beta 版:npx -y paseo-req-kit@beta install;指定版本:npx -y paseo-req-kit@1.13.0 install`;
|
|
41
|
+
|
|
42
|
+
const say = (text = "") => console.log(text);
|
|
43
|
+
const step = (text) => console.log(`\n\x1b[1m==> ${text}\x1b[0m`);
|
|
44
|
+
const ok = (text) => console.log(` \x1b[32m✓\x1b[0m ${text}`);
|
|
45
|
+
const warn = (text) => console.log(` \x1b[33m!\x1b[0m ${text}`);
|
|
46
|
+
class Abort extends Error {}
|
|
47
|
+
const die = (text) => {
|
|
48
|
+
throw new Abort(text);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
// Options
|
|
53
|
+
|
|
54
|
+
export function parseArgs(argv) {
|
|
55
|
+
const opts = { registry: null, saveRegistry: true, configure: true, paseo: true, plugin: "ask", spec: null, help: false };
|
|
56
|
+
for (let i = 0; i < argv.length; i++) {
|
|
57
|
+
const a = argv[i];
|
|
58
|
+
const value = (name) => {
|
|
59
|
+
if (i + 1 >= argv.length || argv[i + 1].startsWith("--")) die(`${name} 后面要写地址,例如 ${name} https://npm.example.internal/`);
|
|
60
|
+
return argv[++i];
|
|
61
|
+
};
|
|
62
|
+
if (a === "--registry") opts.registry = value("--registry");
|
|
63
|
+
else if (a.startsWith("--registry=")) opts.registry = a.slice("--registry=".length);
|
|
64
|
+
else if (a === "--spec") opts.spec = value("--spec");
|
|
65
|
+
else if (a.startsWith("--spec=")) opts.spec = a.slice("--spec=".length);
|
|
66
|
+
else if (a === "--no-save-registry") opts.saveRegistry = false;
|
|
67
|
+
else if (a === "--no-configure") opts.configure = false;
|
|
68
|
+
else if (a === "--skip-paseo") opts.paseo = false;
|
|
69
|
+
else if (a === "--with-plugin") opts.plugin = "yes";
|
|
70
|
+
else if (a === "--no-plugin") opts.plugin = "no";
|
|
71
|
+
else if (a === "-h" || a === "--help") opts.help = true;
|
|
72
|
+
else die(`不认识的选项:${a}(--help 查看用法)`);
|
|
73
|
+
}
|
|
74
|
+
if (opts.registry !== null && !/^https?:\/\/\S+$/.test(opts.registry)) die(`--registry 要写完整的地址,例如 https://npm.example.internal/(现在是 ${opts.registry || "空"})`);
|
|
75
|
+
return opts;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Registry addresses compare equal with or without the trailing slash. */
|
|
79
|
+
export function sameRegistry(a, b) {
|
|
80
|
+
const norm = (u) => String(u ?? "").trim().replace(/\/+$/, "").toLowerCase();
|
|
81
|
+
return norm(a) === norm(b);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// Running things
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* The environment for child commands. Running under npx adds settings meant for npx itself
|
|
89
|
+
* (which package to run, "yes"); they are dropped. The registry is set explicitly.
|
|
90
|
+
*/
|
|
91
|
+
function childEnv(registry) {
|
|
92
|
+
const env = { ...process.env };
|
|
93
|
+
for (const key of Object.keys(env)) {
|
|
94
|
+
const k = key.toLowerCase();
|
|
95
|
+
if (["npm_config_package", "npm_config_yes", "npm_config_call", "npm_config_local_prefix", "npm_command", "npm_package_json"].includes(k) || k.startsWith("npm_lifecycle_")) delete env[key];
|
|
96
|
+
if (k === "npm_config_registry") delete env[key];
|
|
97
|
+
}
|
|
98
|
+
if (registry) env.npm_config_registry = registry;
|
|
99
|
+
return env;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
let ENV = childEnv(null);
|
|
103
|
+
|
|
104
|
+
function quoteForCmd(args) {
|
|
105
|
+
return args.map((a) => (/[\s&()^|<>"]/.test(a) ? `"${a.replace(/"/g, '""')}"` : a));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Runs a command; on Windows .cmd shims (npm, paseo) need the shell. */
|
|
109
|
+
function run(command, args, { inherit = false, env = ENV, timeout = 900_000, input } = {}) {
|
|
110
|
+
const shell = WINDOWS && !/\.exe$/i.test(command);
|
|
111
|
+
const result = spawnSync(command, shell ? quoteForCmd(args) : args, {
|
|
112
|
+
encoding: "utf8",
|
|
113
|
+
windowsHide: true,
|
|
114
|
+
shell,
|
|
115
|
+
env,
|
|
116
|
+
timeout,
|
|
117
|
+
input,
|
|
118
|
+
stdio: inherit ? "inherit" : input !== undefined ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"],
|
|
119
|
+
});
|
|
120
|
+
return { status: result.error ? null : result.status, stdout: result.stdout ?? "", stderr: result.stderr ?? "", error: result.error };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function npm(args, options = {}) {
|
|
124
|
+
const fake = process.env.REQ_KIT_NPM?.trim();
|
|
125
|
+
if (fake) return run(process.execPath, [fake, ...args], options);
|
|
126
|
+
return run(WINDOWS ? "npm.cmd" : "npm", args, options);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const node = (args, options = {}) => run(process.execPath, args, { inherit: true, ...options });
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* The first `name` on PATH (with PATHEXT on Windows), skipping copies inside `skipRoot`: under
|
|
133
|
+
* npx, PATH starts with npx's own folder, which has this temporary copy's req.
|
|
134
|
+
*/
|
|
135
|
+
export function which(name, env = process.env, skipRoot = null) {
|
|
136
|
+
const dirs = (env.PATH ?? env.Path ?? "").split(path.delimiter).filter(Boolean);
|
|
137
|
+
const exts = WINDOWS ? ["", ...(env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").map((e) => e.toLowerCase())] : [""];
|
|
138
|
+
const skip = skipRoot ? safeRealpath(skipRoot) : null;
|
|
139
|
+
for (const dir of dirs) {
|
|
140
|
+
for (const ext of exts) {
|
|
141
|
+
const file = path.join(dir, name + ext);
|
|
142
|
+
try {
|
|
143
|
+
const stat = fs.statSync(file);
|
|
144
|
+
if (!stat.isFile()) continue;
|
|
145
|
+
if (!WINDOWS) fs.accessSync(file, fs.constants.X_OK);
|
|
146
|
+
} catch {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (skip) {
|
|
150
|
+
const real = safeRealpath(file);
|
|
151
|
+
if (real === skip || real.startsWith(skip + path.sep) || dir.split(/[\\/]/).includes("_npx")) continue;
|
|
152
|
+
}
|
|
153
|
+
return file;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function safeRealpath(p) {
|
|
160
|
+
try {
|
|
161
|
+
return fs.realpathSync(p);
|
|
162
|
+
} catch {
|
|
163
|
+
return path.resolve(p);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function writable(dir) {
|
|
168
|
+
try {
|
|
169
|
+
fs.accessSync(dir, fs.constants.W_OK);
|
|
170
|
+
return true;
|
|
171
|
+
} catch {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async function daemonStatus(url) {
|
|
177
|
+
try {
|
|
178
|
+
const response = await fetch(`${url}/api/status`, { signal: AbortSignal.timeout(3000) });
|
|
179
|
+
let version = "";
|
|
180
|
+
try {
|
|
181
|
+
version = (await response.json())?.version ?? "";
|
|
182
|
+
} catch {
|
|
183
|
+
/* any answer means a daemon is listening (even 401 with a password set) */
|
|
184
|
+
}
|
|
185
|
+
return { up: true, version: String(version) };
|
|
186
|
+
} catch {
|
|
187
|
+
return { up: false, version: "" };
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async function ask(question) {
|
|
192
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
193
|
+
try {
|
|
194
|
+
return (await rl.question(question)).trim();
|
|
195
|
+
} catch {
|
|
196
|
+
return "n";
|
|
197
|
+
} finally {
|
|
198
|
+
rl.close();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const interactive = () => Boolean(process.stdin.isTTY) && process.env.REQ_KIT_NO_TTY !== "1";
|
|
203
|
+
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
// The install
|
|
206
|
+
|
|
207
|
+
async function main(argv) {
|
|
208
|
+
const opts = parseArgs(argv);
|
|
209
|
+
if (opts.help) {
|
|
210
|
+
say(HELP);
|
|
211
|
+
return 0;
|
|
212
|
+
}
|
|
213
|
+
const spec = opts.spec ?? process.env.REQ_KIT_INSTALL_SPEC?.trim() ?? `${PKG.name}@${PKG.version}`;
|
|
214
|
+
say(`安装 req-kit ${PKG.version}:Paseo 上的需求开发体系`);
|
|
215
|
+
|
|
216
|
+
// -------------------------------------------------------------------------
|
|
217
|
+
step("检查环境");
|
|
218
|
+
const nodeMajor = Number(process.versions.node.split(".")[0]);
|
|
219
|
+
if (nodeMajor < 22) die(`需要 Node.js 22 或更新版本,当前是 ${process.version}。请升级 Node.js 后重新运行`);
|
|
220
|
+
ok(`Node.js ${process.version}`);
|
|
221
|
+
const npmVersion = npm(["--version"], { timeout: 60_000 });
|
|
222
|
+
if (npmVersion.status !== 0) die("没有找到 npm(通常随 Node.js 一起安装)");
|
|
223
|
+
ok(`npm ${npmVersion.stdout.trim() || "?"}`);
|
|
224
|
+
const git = run(WINDOWS ? "git.exe" : "git", ["--version"], { timeout: 30_000 });
|
|
225
|
+
if (git.status === 0) ok(`git ${git.stdout.trim().replace(/^git version /, "")}`);
|
|
226
|
+
else warn("没有找到 git:流水线需要在 git 仓库中工作,请稍后安装");
|
|
227
|
+
|
|
228
|
+
// -------------------------------------------------------------------------
|
|
229
|
+
step("npm 源");
|
|
230
|
+
// `npx --registry=…` passes it on as npm_config_registry; --registry says it explicitly.
|
|
231
|
+
const wanted = opts.registry ?? process.env.npm_config_registry?.trim() ?? process.env.NPM_CONFIG_REGISTRY?.trim() ?? null;
|
|
232
|
+
const savedResult = npm(["config", "get", "registry"], { env: childEnv(null), timeout: 60_000 });
|
|
233
|
+
const saved = savedResult.status === 0 ? savedResult.stdout.trim() || DEFAULT_REGISTRY : DEFAULT_REGISTRY;
|
|
234
|
+
let registry = saved;
|
|
235
|
+
if (wanted && !sameRegistry(wanted, saved)) {
|
|
236
|
+
registry = wanted;
|
|
237
|
+
if (opts.saveRegistry) {
|
|
238
|
+
const set = npm(["config", "set", "registry", wanted], { env: childEnv(null), timeout: 60_000 });
|
|
239
|
+
if (set.status === 0) ok(`已把本机的 npm 源设为 ${wanted}(原来是 ${saved})。以后 req upgrade 也从这里取`);
|
|
240
|
+
else warn(`没能保存 npm 源(${(set.stderr || set.stdout).trim().split("\n").at(-1) ?? ""}),这次安装照常使用 ${wanted}。之后可以运行:npm config set registry ${wanted}`);
|
|
241
|
+
} else {
|
|
242
|
+
ok(`这次安装使用 ${wanted}(没有保存,本机的 npm 源仍是 ${saved})`);
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
ok(`npm 源:${saved}`);
|
|
246
|
+
}
|
|
247
|
+
ENV = childEnv(registry);
|
|
248
|
+
if (sameRegistry(registry, DEFAULT_REGISTRY)) say(" 公司内网请改用镜像:npx -y --registry=<镜像地址> paseo-req-kit@latest install");
|
|
249
|
+
|
|
250
|
+
// -------------------------------------------------------------------------
|
|
251
|
+
step(`安装 ${PKG.name} ${PKG.version}`);
|
|
252
|
+
if (/^[@a-z0-9][^\s]*@[^/\\\s]+$/i.test(spec) && !/^[a-z]+:/i.test(spec)) {
|
|
253
|
+
const found = npm(["view", spec, "version"], { timeout: 120_000 });
|
|
254
|
+
if (found.status !== 0 || !found.stdout.trim()) {
|
|
255
|
+
die(`npm 源 ${registry} 上找不到 ${spec}。镜像可能还没有同步这个版本,稍后再试;或用 --registry 换一个 npm 源`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
let prefix = npm(["prefix", "-g"], { timeout: 60_000 }).stdout.trim();
|
|
259
|
+
let ownPrefix = false;
|
|
260
|
+
const root = typeof process.getuid === "function" && process.getuid() === 0;
|
|
261
|
+
if (!prefix || (!root && !writable(prefix))) {
|
|
262
|
+
// A system Node.js (e.g. /usr/local) needs sudo for global installs; use a directory of our own.
|
|
263
|
+
prefix = path.join(os.homedir(), ".req-kit", "npm");
|
|
264
|
+
ownPrefix = true;
|
|
265
|
+
fs.mkdirSync(prefix, { recursive: true });
|
|
266
|
+
warn(`npm 的全局目录不可写,改装到 ${prefix}(不需要 sudo)`);
|
|
267
|
+
}
|
|
268
|
+
const prefixArgs = ownPrefix ? ["--prefix", prefix] : [];
|
|
269
|
+
const installed = npm(["install", "-g", ...prefixArgs, spec], { inherit: true });
|
|
270
|
+
if (installed.status !== 0) die("npm 安装失败(见上面的输出)。公司内网请确认 npm 源是镜像地址(--registry)");
|
|
271
|
+
const npmRoot = npm(["root", "-g", ...prefixArgs], { timeout: 60_000 }).stdout.trim();
|
|
272
|
+
const reqMjs = path.join(npmRoot, PKG.name, "bin", "req.mjs");
|
|
273
|
+
if (!fs.existsSync(reqMjs)) die(`安装后没有找到 ${reqMjs}`);
|
|
274
|
+
ok(`已安装到 ${path.join(npmRoot, PKG.name)}`);
|
|
275
|
+
const binDir = WINDOWS ? prefix : path.join(prefix, "bin");
|
|
276
|
+
const installedPkg = JSON.parse(fs.readFileSync(path.join(npmRoot, PKG.name, "package.json"), "utf8"));
|
|
277
|
+
const req = (args, options = {}) => node([reqMjs, ...args], options);
|
|
278
|
+
|
|
279
|
+
// -------------------------------------------------------------------------
|
|
280
|
+
// Paseo: req-kit runs on a Paseo daemon on this machine.
|
|
281
|
+
const daemonUrl = process.env.REQ_KIT_PASEO_URL?.trim() || "http://127.0.0.1:6767";
|
|
282
|
+
let paseoCmd = null;
|
|
283
|
+
if (opts.paseo) {
|
|
284
|
+
step("检查 Paseo");
|
|
285
|
+
const range = installedPkg.reqKit?.paseo ?? "";
|
|
286
|
+
const minor = /(\d+\.\d+)/.exec(range)?.[1] ?? "";
|
|
287
|
+
if ("REQ_KIT_PASEO_BIN" in process.env) {
|
|
288
|
+
const bin = process.env.REQ_KIT_PASEO_BIN;
|
|
289
|
+
if (bin && fs.existsSync(bin)) paseoCmd = bin;
|
|
290
|
+
} else {
|
|
291
|
+
paseoCmd = which("paseo", ENV);
|
|
292
|
+
}
|
|
293
|
+
const paseo = (args, options) => run(paseoCmd, args, { timeout: 120_000, ...options });
|
|
294
|
+
|
|
295
|
+
const status = await daemonStatus(daemonUrl);
|
|
296
|
+
if (!paseoCmd && status.up) {
|
|
297
|
+
// The desktop app runs its own daemon and does not put the paseo command on PATH.
|
|
298
|
+
ok(`Paseo daemon 已在运行${status.version ? `(${status.version})` : ""},没有 paseo 命令(通常是桌面版 Paseo 管理的 daemon)`);
|
|
299
|
+
warn("需要网页界面时,在桌面版的设置中开启,或安装 Paseo 命令行后运行 paseo daemon config set features.webUi.enabled true");
|
|
300
|
+
} else {
|
|
301
|
+
if (!paseoCmd) {
|
|
302
|
+
say(` 没有找到 Paseo,安装 Paseo ${minor} 系列的最新版(@getpaseo/cli@~${minor}.0,这个版本的 req-kit 支持 ${range})`);
|
|
303
|
+
const result = npm(["install", "-g", ...prefixArgs, `@getpaseo/cli@~${minor}.0`], { inherit: true });
|
|
304
|
+
if (result.status !== 0) die("Paseo 安装失败(见上面的输出)。也可以自己安装 Paseo 后重新运行");
|
|
305
|
+
const candidates = WINDOWS ? [path.join(binDir, "paseo.cmd"), path.join(binDir, "paseo")] : [path.join(binDir, "paseo")];
|
|
306
|
+
paseoCmd = candidates.find((c) => fs.existsSync(c)) ?? which("paseo", ENV) ?? candidates[0];
|
|
307
|
+
ok("已安装 Paseo");
|
|
308
|
+
}
|
|
309
|
+
const v = /(\d+\.\d+\.\d+)/.exec(paseo(["--version"]).stdout)?.[1];
|
|
310
|
+
if (v) {
|
|
311
|
+
if (v.startsWith(`${minor}.`)) ok(`Paseo ${v}`);
|
|
312
|
+
else warn(`Paseo 是 ${v},这个版本的 req-kit 支持 ${range}。请换成匹配的 Paseo,或用 req upgrade 选择支持它的 req-kit`);
|
|
313
|
+
}
|
|
314
|
+
let changed = false;
|
|
315
|
+
if (/"value":\s*true/.test(paseo(["daemon", "config", "get", "features.webUi.enabled"]).stdout)) ok("Paseo 网页界面已开启");
|
|
316
|
+
else if (paseo(["daemon", "config", "set", "features.webUi.enabled", "true"]).status === 0) {
|
|
317
|
+
ok("已开启 Paseo 自带的网页界面(features.webUi.enabled)");
|
|
318
|
+
changed = true;
|
|
319
|
+
} else warn("没能开启 Paseo 的网页界面,之后可以运行:paseo daemon config set features.webUi.enabled true");
|
|
320
|
+
|
|
321
|
+
if ((await daemonStatus(daemonUrl)).up) {
|
|
322
|
+
if (changed) warn("Paseo daemon 正在运行,网页界面要在重启后生效:paseo daemon restart(会中断正在运行的 agent,方便时再重启)");
|
|
323
|
+
else ok("Paseo daemon 正在运行");
|
|
324
|
+
} else if (paseo(["daemon", "start"]).status === 0) {
|
|
325
|
+
// 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 查看原因");
|
|
330
|
+
say(` Paseo 网页界面:${daemonUrl}/`);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const agents = ["claude", "codex", "opencode", "pi", "copilot"].filter((a) => which(a, ENV));
|
|
334
|
+
if (agents.length > 0) ok(`agent 命令行:${agents.join(" ")}`);
|
|
335
|
+
else warn("没有找到任何 agent 命令行(Claude Code、Codex、OpenCode、Pi、Copilot)。Paseo 要用它们干活:至少装一个并登录,再运行 req configure");
|
|
336
|
+
}
|
|
337
|
+
const reqEnv = paseoCmd ? { ...ENV, REQ_KIT_PASEO_BIN: paseoCmd } : ENV;
|
|
338
|
+
|
|
339
|
+
// -------------------------------------------------------------------------
|
|
340
|
+
step("启用这个版本,安装 /req 等 skill");
|
|
341
|
+
const kitHome = process.env.REQ_KIT_HOME?.trim() ? path.resolve(process.env.REQ_KIT_HOME.trim()) : path.join(os.homedir(), ".req-kit");
|
|
342
|
+
const config = path.join(kitHome, "config.json");
|
|
343
|
+
const unfilled = () => {
|
|
344
|
+
try {
|
|
345
|
+
return fs.readFileSync(config, "utf8").includes("CHANGE-ME");
|
|
346
|
+
} catch {
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
// On a new machine setup ends with "fill in the agents" (✗), which the wizard below does.
|
|
351
|
+
if (req(["setup"], { env: reqEnv }).status !== 0 && !unfilled()) die("req setup 没有完成(见上面的输出)。修复后可以重新运行安装命令");
|
|
352
|
+
|
|
353
|
+
// -------------------------------------------------------------------------
|
|
354
|
+
if (opts.configure && unfilled()) {
|
|
355
|
+
step("选择 agent");
|
|
356
|
+
// No questions: the logged-in agents are picked as recommended; change them later with
|
|
357
|
+
// req configure or in Paseo (Settings → Plugins → req-kit).
|
|
358
|
+
if (req(["configure", "--auto"], { env: reqEnv }).status === 0) req(["check"], { env: reqEnv });
|
|
359
|
+
else if (interactive()) {
|
|
360
|
+
warn("没能自动选好 agent,改为逐项选择");
|
|
361
|
+
if (req(["configure"], { env: reqEnv }).status === 0) req(["check"], { env: reqEnv });
|
|
362
|
+
else warn("配置向导没有完成,之后可以运行 req configure");
|
|
363
|
+
} else warn("没能自动选好 agent(Paseo 在运行、至少登录一种 agent 后再试)。之后运行:req configure --auto");
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// -------------------------------------------------------------------------
|
|
367
|
+
// The req-kit plugin for Paseo. Already installed: req setup above refreshed it.
|
|
368
|
+
const pluginDir = path.join(kitHome, "paseo-plugin");
|
|
369
|
+
if (opts.plugin !== "no" && !fs.existsSync(path.join(pluginDir, "paseo-plugin.json")) && (paseoCmd || opts.plugin === "yes")) {
|
|
370
|
+
step("Paseo 插件");
|
|
371
|
+
say(" req-kit 插件在 Paseo(网页端、桌面版、手机)中显示需求总览、进度和文档,并可以确认方案、停止、继续。");
|
|
372
|
+
const fail = () => warn("插件没有装好(见上面的输出),之后可以运行 req plugin install");
|
|
373
|
+
if (opts.plugin === "yes") {
|
|
374
|
+
if (req(["plugin", "install", "--yes"], { env: reqEnv }).status !== 0) fail();
|
|
375
|
+
} else if (interactive()) {
|
|
376
|
+
const answer = await ask(" 安装 req-kit 插件?[Y/n] ");
|
|
377
|
+
if (/^n(o)?$/i.test(answer)) say(" 跳过。之后可以运行 req plugin install");
|
|
378
|
+
else if (req(["plugin", "install"], { env: reqEnv }).status !== 0) fail();
|
|
379
|
+
} else say(" 没有可交互的终端,跳过。之后运行:req plugin install");
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// -------------------------------------------------------------------------
|
|
383
|
+
step("完成");
|
|
384
|
+
const found = which("req", ENV, KIT_ROOT === path.join(npmRoot, PKG.name) ? null : KIT_ROOT);
|
|
385
|
+
const expected = [path.join(binDir, "req"), path.join(binDir, "req.cmd")].map(safeRealpath);
|
|
386
|
+
if (found && expected.includes(safeRealpath(found))) ok(`req 命令可用:${found}`);
|
|
387
|
+
else if (!found) {
|
|
388
|
+
if (WINDOWS) warn(`req 命令还不在 PATH 中。把 ${binDir} 加到用户环境变量 Path 中,然后重新打开终端`);
|
|
389
|
+
else {
|
|
390
|
+
warn("req 命令还不在 PATH 中。把下面这行加到 ~/.bashrc 或 ~/.zshrc,然后重新打开终端:");
|
|
391
|
+
say(` export PATH="${binDir}:$PATH"`);
|
|
392
|
+
}
|
|
393
|
+
} else warn(`PATH 中的 req 是 ${found},不是刚装的这个;可以改用同名命令 req-kit,或把 ${binDir} 放到 PATH 的前面`);
|
|
394
|
+
req(["version"], { env: reqEnv });
|
|
395
|
+
|
|
396
|
+
say("");
|
|
397
|
+
say("下一步:");
|
|
398
|
+
say(` 1. 运行 req doctor 检查 Paseo daemon 和 agent 是否就绪;在浏览器打开 ${daemonUrl}/ 就是 Paseo 的网页界面`);
|
|
399
|
+
say(" 2. 在项目的 Paseo 对话中运行 /req-setup 接入项目(每个项目一次)");
|
|
400
|
+
say(" 3. 在 Paseo 对话中输入 /req 和一句话需求,例如:/req 登录页增加短信验证码登录");
|
|
401
|
+
say("文档:运行 req docs 在本机打开(和装的版本一致,不需要外网)");
|
|
402
|
+
return 0;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const isMain = process.argv[1] && safeRealpath(process.argv[1]) === safeRealpath(fileURLToPath(import.meta.url));
|
|
406
|
+
if (isMain) {
|
|
407
|
+
main(process.argv.slice(2)).then(
|
|
408
|
+
(code) => process.exit(code),
|
|
409
|
+
(error) => {
|
|
410
|
+
if (error instanceof Abort) {
|
|
411
|
+
console.error(`\n\x1b[31m✗\x1b[0m ${error.message}`);
|
|
412
|
+
process.exit(1);
|
|
413
|
+
}
|
|
414
|
+
console.error(error?.stack ?? String(error));
|
|
415
|
+
process.exit(1);
|
|
416
|
+
},
|
|
417
|
+
);
|
|
418
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Types for the parts of req-docs.mjs the tests use.
|
|
2
|
+
import type { Server } from "node:http";
|
|
3
|
+
export function siteDir(root?: string, env?: NodeJS.ProcessEnv | Record<string, string>): string | null;
|
|
4
|
+
export function resolveFile(dir: string, urlPath: string): string | null;
|
|
5
|
+
export function createServer(dir: string): Server;
|