paseo-req-kit 1.14.0-beta.2 → 1.14.0-beta.4
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 +25 -0
- package/bin/req.mjs +16 -0
- package/contracts/cli-json.md +1 -1
- package/docs/guide//344/275/277/347/224/250/350/200/205/346/214/207/345/215/227.md +15 -0
- package/engine/prompts/06-code-review.md +12 -1
- package/engine/prompts/incremental.md +2 -0
- package/engine/src/api.ts +18 -2
- package/engine/src/checklist-cmd.ts +37 -0
- package/engine/src/checklist.ts +254 -0
- package/engine/src/cli-args.ts +4 -1
- package/engine/src/code-review.ts +419 -0
- package/engine/src/codeowners.ts +87 -0
- package/engine/src/main.ts +13 -3
- package/engine/src/preflight.ts +10 -1
- package/engine/src/reply.ts +7 -4
- package/engine/src/review-assets/check.css +57 -0
- package/engine/src/review-assets/check.html +35 -0
- package/engine/src/review-assets/check.js +258 -0
- package/engine/src/review-assets/code.css +93 -0
- package/engine/src/review-assets/code.html +43 -0
- package/engine/src/review-assets/code.js +402 -0
- package/engine/src/review.ts +177 -2
- package/engine/src/try.ts +134 -0
- package/package.json +1 -1
- package/paseo-plugin/client/data.ts +3 -3
- package/paseo-plugin/client/detail.tsx +10 -2
- package/paseo-plugin/client/nav.ts +10 -0
- package/paseo-plugin/client/review.tsx +30 -6
- package/paseo-plugin/package.json +1 -1
- package/paseo-plugin/server/handlers.ts +2 -2
- package/paseo-plugin/shared/contracts.ts +4 -1
- package/site-dist/404.html +2 -2
- package/site-dist/assets/{app.gI1JF182.js → app.Cp7WwPWE.js} +1 -1
- package/site-dist/assets/changelog.md.Zi-_3Rjh.js +1 -0
- package/site-dist/assets/chunks/@localSearchIndexroot.COxp5Rrc.js +1 -0
- package/site-dist/assets/chunks/{VPLocalSearchBox.DNmhkkfS.js → VPLocalSearchBox.CMTXQK-Q.js} +1 -1
- package/site-dist/assets/chunks/{theme.CZzkCOAC.js → theme.DvVUfKr3.js} +2 -2
- package/site-dist/assets/{guide_getting-started.md.zmtXv15G.js → guide_getting-started.md.D8XlIQOg.js} +1 -1
- package/site-dist/assets/{guide_user.md.2v7b_fzd.js → guide_user.md.B4N9ywmr.js} +1 -1
- package/site-dist/assets/{guide_user.md.2v7b_fzd.lean.js → guide_user.md.B4N9ywmr.lean.js} +1 -1
- package/site-dist/assets/{reference_contracts_cli-json.md.BR6gwq-e.js → reference_contracts_cli-json.md.CCvKLasM.js} +1 -1
- package/site-dist/assets/{reference_contracts_cli-json.md.BR6gwq-e.lean.js → reference_contracts_cli-json.md.CCvKLasM.lean.js} +1 -1
- package/site-dist/changelog.html +5 -5
- package/site-dist/guide/getting-started.html +5 -5
- package/site-dist/guide/how-it-works.html +4 -4
- package/site-dist/guide/index.html +4 -4
- package/site-dist/guide/install.html +4 -4
- package/site-dist/guide/maintainer.html +4 -4
- package/site-dist/guide/phases.html +4 -4
- package/site-dist/guide/project.html +4 -4
- package/site-dist/guide/user.html +5 -5
- package/site-dist/hashmap.json +1 -1
- package/site-dist/index.html +4 -4
- package/site-dist/reference/adr/0001-step-agents-under-origin-chat.html +4 -4
- package/site-dist/reference/adr/0002-no-directory-switch-in-chat.html +4 -4
- package/site-dist/reference/adr/0003-script-engine-not-hub.html +4 -4
- package/site-dist/reference/adr/0004-result-files.html +4 -4
- package/site-dist/reference/adr/0005-no-default-branch-local-commits-only.html +4 -4
- package/site-dist/reference/adr/0006-skill-install-targets.html +4 -4
- package/site-dist/reference/adr/0007-own-interview-skill.html +4 -4
- package/site-dist/reference/adr/0008-entries-do-not-call-entries.html +4 -4
- package/site-dist/reference/adr/0009-versioned-install-layout.html +4 -4
- package/site-dist/reference/adr/0010-generated-feature-overview.html +4 -4
- package/site-dist/reference/adr/0011-resume-after-completion.html +4 -4
- package/site-dist/reference/adr/0012-review-panel.html +4 -4
- package/site-dist/reference/adr/0013-no-release-tooling.html +4 -4
- package/site-dist/reference/adr/0014-notifier-slot.html +4 -4
- package/site-dist/reference/adr/0015-incremental-continue.html +4 -4
- package/site-dist/reference/adr/0016-upgrade-from-tags.html +4 -4
- package/site-dist/reference/adr/0017-use-paseo-capabilities.html +4 -4
- package/site-dist/reference/adr/0018-own-worktree-option.html +4 -4
- package/site-dist/reference/adr/0019-parallel-orchestration.html +4 -4
- package/site-dist/reference/adr/0020-scheduling-through-the-chat.html +4 -4
- package/site-dist/reference/adr/0021-npm-version-manager.html +4 -4
- package/site-dist/reference/adr/0022-develop-and-verify-phases.html +4 -4
- package/site-dist/reference/adr/0023-types-gate-slices.html +4 -4
- package/site-dist/reference/adr/0024-confirm-early-review-axes.html +4 -4
- package/site-dist/reference/adr/0025-joint-requirements.html +4 -4
- package/site-dist/reference/adr/0026-machine-records-and-api.html +4 -4
- package/site-dist/reference/adr/0027-paseo-plugin.html +4 -4
- package/site-dist/reference/adr/0028-external-integrations.html +4 -4
- package/site-dist/reference/adr/0029-requirements-from-cards.html +4 -4
- package/site-dist/reference/adr/0030-card-intake-process.html +4 -4
- package/site-dist/reference/adr/0031-fewer-interventions.html +4 -4
- package/site-dist/reference/adr/0032-npm-only-install.html +4 -4
- package/site-dist/reference/adr/0033-prototype-review-and-ui-check.html +4 -4
- package/site-dist/reference/adr/index.html +4 -4
- package/site-dist/reference/commands.html +4 -4
- package/site-dist/reference/config.html +4 -4
- package/site-dist/reference/contracts/cli-json.html +5 -5
- package/site-dist/reference/contracts/feature-files.html +4 -4
- package/site-dist/reference/contracts/index.html +4 -4
- package/site-dist/reference/contracts/notifier-event.html +4 -4
- package/site-dist/reference/contracts/result-file.html +4 -4
- package/site-dist/reference/contracts/spec.html +4 -4
- package/site-dist/reference/contracts/state.html +4 -4
- package/site-dist/reference/contracts/usage-record.html +4 -4
- package/site-dist/reference/contracts/verify.html +4 -4
- package/site-dist/reference/scope.html +4 -4
- package/skills/req/SKILL.md +3 -0
- package/skills/req/playbooks/accept.md +2 -2
- package/site-dist/assets/changelog.md.2xf690VW.js +0 -1
- package/site-dist/assets/chunks/@localSearchIndexroot.CiFeI6qC.js +0 -1
- /package/site-dist/assets/{changelog.md.2xf690VW.lean.js → changelog.md.Zi-_3Rjh.lean.js} +0 -0
- /package/site-dist/assets/{guide_getting-started.md.zmtXv15G.lean.js → guide_getting-started.md.D8XlIQOg.lean.js} +0 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// `req try <需求> [--stop]` (1.14): runs a finished requirement so the requester can try it,
|
|
2
|
+
// without knowing how to start the project. The project's paseo.json services are started in the
|
|
3
|
+
// requirement's own workspace through Paseo (each on its own port), and the addresses are given
|
|
4
|
+
// with what the handoff note says about test accounts and data. Projects without Paseo-managed
|
|
5
|
+
// services get the start instructions the pipeline wrote down, and the advice to ask the chat.
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { section } from "./checklist.js";
|
|
9
|
+
import { DEFAULT_CONFIG, loadConfig, type PipelineConfig } from "./config.js";
|
|
10
|
+
import { defaultConfigPath } from "./locations.js";
|
|
11
|
+
import type { AgentRuntime, ServiceTarget } from "./runtime.js";
|
|
12
|
+
import { isAlive, readState, statePath } from "./state.js";
|
|
13
|
+
|
|
14
|
+
export interface TryResult {
|
|
15
|
+
ok: boolean;
|
|
16
|
+
message: string;
|
|
17
|
+
services: Array<{ name: string; url: string | null; running: boolean; output: string | null }>;
|
|
18
|
+
/** How to start it and what test data to use, from handoff.md / service-start.md. */
|
|
19
|
+
howTo: string[];
|
|
20
|
+
stopped?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function read(file: string): string {
|
|
24
|
+
try {
|
|
25
|
+
return fs.readFileSync(file, "utf8");
|
|
26
|
+
} catch {
|
|
27
|
+
return "";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const tidy = (lines: string[]) => lines.map((l) => l.replace(/\s+$/, "")).filter((l, i, all) => l.trim() || (i > 0 && all[i - 1].trim())).slice(0, 40);
|
|
32
|
+
|
|
33
|
+
/** What the pipeline wrote about running it and the data to use. */
|
|
34
|
+
export function howToTry(featureAbs: string): string[] {
|
|
35
|
+
const handoff = read(path.join(featureAbs, "handoff.md"));
|
|
36
|
+
const run = section(handoff, /怎么跑起来|如何运行|启动/);
|
|
37
|
+
const data = section(handoff, /测试需要的准备|测试数据|账号/);
|
|
38
|
+
const start = run.length ? run : section(read(path.join(featureAbs, "service-start.md")), /启动方法|怎么启动|启动/);
|
|
39
|
+
return tidy([...(start.length ? ["怎么跑起来:", ...start] : []), ...(data.length ? ["", "测试需要的准备:", ...data] : [])]);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface TryRecord {
|
|
43
|
+
at: string;
|
|
44
|
+
target: ServiceTarget;
|
|
45
|
+
names: string[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const recordFile = (featureAbs: string) => path.join(featureAbs, ".pipeline", "try.json");
|
|
49
|
+
|
|
50
|
+
export async function tryRequirement(input: { repo: string; featureDir: string; stop?: boolean; runtime: Pick<AgentRuntime, "listServices" | "startServices" | "stopServices">; config: PipelineConfig }): Promise<TryResult> {
|
|
51
|
+
const featureAbs = path.join(input.repo, input.featureDir);
|
|
52
|
+
const state = readState(statePath(input.repo, input.featureDir));
|
|
53
|
+
const howTo = howToTry(featureAbs);
|
|
54
|
+
if (input.stop) {
|
|
55
|
+
let rec: TryRecord | null = null;
|
|
56
|
+
try {
|
|
57
|
+
rec = JSON.parse(fs.readFileSync(recordFile(featureAbs), "utf8")) as TryRecord;
|
|
58
|
+
} catch {
|
|
59
|
+
rec = null;
|
|
60
|
+
}
|
|
61
|
+
if (!rec) return { ok: true, message: "没有由试用启动的服务,不用停止。", services: [], howTo, stopped: false };
|
|
62
|
+
await input.runtime.stopServices(rec.target, rec.names);
|
|
63
|
+
fs.rmSync(recordFile(featureAbs), { force: true });
|
|
64
|
+
return { ok: true, message: `已停止试用的服务:${rec.names.join("、")}。`, services: [], howTo, stopped: true };
|
|
65
|
+
}
|
|
66
|
+
if (state && state.status === "running" && isAlive(state.pid)) {
|
|
67
|
+
return { ok: false, message: "流水线还在运行,它会自己启动和停止服务。等开发结束后再试用。", services: [], howTo };
|
|
68
|
+
}
|
|
69
|
+
const target: ServiceTarget | null = state?.worktree?.workspaceId ? { workspaceId: state.worktree.workspaceId } : state?.originAgentId ? { agentId: state.originAgentId } : null;
|
|
70
|
+
const names = target ? await input.runtime.listServices(target).catch(() => null) : null;
|
|
71
|
+
if (!target || !names || names.length === 0) {
|
|
72
|
+
return {
|
|
73
|
+
ok: false,
|
|
74
|
+
message:
|
|
75
|
+
"这个项目没有用 Paseo 管理的服务脚本(paseo.json),不能一键启动。可以在这个需求的对话里说\"帮我把它跑起来\",或按下面的方法启动;以后请开发同事运行 /req-setup 写好 paseo.json,就能一键试用。",
|
|
76
|
+
services: [],
|
|
77
|
+
howTo,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const started = await input.runtime.startServices(target, names, input.config.serviceReadySeconds * 1000);
|
|
81
|
+
fs.mkdirSync(path.dirname(recordFile(featureAbs)), { recursive: true });
|
|
82
|
+
fs.writeFileSync(recordFile(featureAbs), JSON.stringify({ at: new Date().toISOString(), target, names } satisfies TryRecord, null, 2));
|
|
83
|
+
const failed = started.filter((s) => !s.running);
|
|
84
|
+
const services = started.map((s) => ({ name: s.name, url: s.url, running: s.running, output: s.running ? null : s.output }));
|
|
85
|
+
const ok = failed.length === 0;
|
|
86
|
+
const lines = started.map((s) => (s.running ? `- ${s.name}:${s.url ?? "已启动(没有网页地址)"}` : `- ${s.name}:没有启动成功`));
|
|
87
|
+
return {
|
|
88
|
+
ok,
|
|
89
|
+
message: `${ok ? "已启动,可以试用了" : "有服务没有启动成功"}:\n${lines.join("\n")}\n用完后停止:req try ${path.basename(input.featureDir)} --stop(插件里点"停止试用")。`,
|
|
90
|
+
services,
|
|
91
|
+
howTo,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function main(argv: string[]): Promise<number> {
|
|
96
|
+
const stop = argv.includes("--stop");
|
|
97
|
+
const json = argv.includes("--json");
|
|
98
|
+
const feature = argv.find((a) => !a.startsWith("--"));
|
|
99
|
+
if (!feature || argv.includes("--help") || argv.includes("-h")) {
|
|
100
|
+
console.log("用法:req try <需求> [--stop]\n\n把做好的需求在它自己的工作区里跑起来,给出访问地址和测试数据,方便亲手试用;--stop 停止。");
|
|
101
|
+
return feature ? 0 : 2;
|
|
102
|
+
}
|
|
103
|
+
const { locate } = await import("./api.js");
|
|
104
|
+
const { repo, featureDir } = locate(process.cwd(), feature);
|
|
105
|
+
let config: PipelineConfig;
|
|
106
|
+
try {
|
|
107
|
+
config = loadConfig(defaultConfigPath(), repo).config;
|
|
108
|
+
} catch {
|
|
109
|
+
config = DEFAULT_CONFIG;
|
|
110
|
+
}
|
|
111
|
+
const { loadRuntimeFactory } = await import("./runtime-loader.js");
|
|
112
|
+
const runtime = await (await loadRuntimeFactory())({ config, log: () => undefined });
|
|
113
|
+
try {
|
|
114
|
+
const result = await tryRequirement({ repo, featureDir, stop, runtime, config });
|
|
115
|
+
if (json) console.log(JSON.stringify(result));
|
|
116
|
+
else {
|
|
117
|
+
console.log(result.message);
|
|
118
|
+
if (result.howTo.length) console.log(`\n${result.howTo.join("\n")}`);
|
|
119
|
+
}
|
|
120
|
+
return result.ok ? 0 : 1;
|
|
121
|
+
} finally {
|
|
122
|
+
await runtime.close().catch(() => undefined);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === path.resolve(import.meta.filename)) {
|
|
127
|
+
main(process.argv.slice(2)).then(
|
|
128
|
+
(code) => process.exit(code),
|
|
129
|
+
(error: Error) => {
|
|
130
|
+
console.error(`没有启动:${error.message}`);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
},
|
|
133
|
+
);
|
|
134
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "paseo-req-kit",
|
|
3
|
-
"version": "1.14.0-beta.
|
|
3
|
+
"version": "1.14.0-beta.4",
|
|
4
4
|
"description": "req-kit: a requirement-development workflow for Paseo. /req aligns a requirement in chat; the engine runs design, implementation, review and verification as Paseo agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -74,11 +74,11 @@ export function useConfigView(repo: string | null) {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
/** The prototype review page's address (1.13); it stays valid while its server runs. */
|
|
77
|
-
export function useReviewUrl(repo: string, featureDir: string, enabled: boolean) {
|
|
77
|
+
export function useReviewUrl(repo: string, featureDir: string, enabled: boolean, page: "prototype" | "check" | "code" = "prototype") {
|
|
78
78
|
const reviewUrl = useRpc(reviewUrlRpc);
|
|
79
79
|
return useQuery({
|
|
80
|
-
queryKey: ["req", "review-url", repo, featureDir],
|
|
81
|
-
queryFn: () => reviewUrl({ repo, featureDir }),
|
|
80
|
+
queryKey: ["req", "review-url", repo, featureDir, page],
|
|
81
|
+
queryFn: () => reviewUrl({ repo, featureDir, ...(page !== "prototype" ? { page } : {}) }),
|
|
82
82
|
enabled: enabled && Boolean(repo && featureDir),
|
|
83
83
|
retry: false,
|
|
84
84
|
});
|
|
@@ -9,7 +9,7 @@ import type { Action, RequirementDetail, Step } from "../shared/contracts";
|
|
|
9
9
|
import { useAction, useDocument, usePreferences, useRequirement, useRequirements } from "./data";
|
|
10
10
|
import { ACTION_TEXT, actionsFor, ago, ATTENTION_LABELS, facts, FILE_LABELS, minutes, tokens } from "./format";
|
|
11
11
|
import { Markdown, tsvToMarkdown } from "./markdown";
|
|
12
|
-
import { ReviewButton } from "./review";
|
|
12
|
+
import { ChecklistButton, CodeReviewButton, ReviewButton } from "./review";
|
|
13
13
|
import { Badge, Button, Chip, Status, useStyles } from "./ui";
|
|
14
14
|
|
|
15
15
|
type Theme = PluginHostProps["theme"];
|
|
@@ -168,6 +168,12 @@ export function RequirementView({
|
|
|
168
168
|
if (!d) return <Status theme={theme} loading={query.isPending} error={query.error} />;
|
|
169
169
|
const steps = allSteps ? d.steps : d.steps.slice(-12);
|
|
170
170
|
const actions = readOnly ? [] : actionsFor(d);
|
|
171
|
+
// 1.14: finished (it has a report) and not running: the requester can try it and accept it.
|
|
172
|
+
const canAccept = d.files.includes("report.md") && !d.running;
|
|
173
|
+
// 1.14: code written and reviewed by the AI: a person can review it too.
|
|
174
|
+
const canReviewCode = (d.files.includes("code-review.md") || d.files.includes("implementation.md")) && !d.running;
|
|
175
|
+
const hr = d.humanReview ?? null;
|
|
176
|
+
const reviewLabel = !hr ? "代码审查" : hr.status === "approved" ? (hr.stale ? "代码审查(有新改动,需重新评审)" : "代码审查(已通过)") : "代码审查(已退回修改)";
|
|
171
177
|
return (
|
|
172
178
|
<View style={{ gap: 16 }}>
|
|
173
179
|
<View style={{ gap: 4 }}>
|
|
@@ -194,8 +200,10 @@ export function RequirementView({
|
|
|
194
200
|
{d.attention === "business" ? <Text style={s.muted}>需求方的决定请在发起需求的对话中回答,回答后再继续。</Text> : null}
|
|
195
201
|
</View>
|
|
196
202
|
|
|
197
|
-
{actions.length > 0 || d.hasPrototype ? (
|
|
203
|
+
{actions.length > 0 || d.hasPrototype || canAccept || canReviewCode ? (
|
|
198
204
|
<View style={s.row}>
|
|
205
|
+
{canReviewCode ? <CodeReviewButton theme={theme} repo={d.sourceRepo ?? d.repo} featureDir={d.featureDir} title={d.title} label={reviewLabel} primary={!!hr && hr.stale} /> : null}
|
|
206
|
+
{canAccept ? <ChecklistButton theme={theme} repo={d.sourceRepo ?? d.repo} featureDir={d.featureDir} title={d.title} primary={d.stage.startsWith("待验收") || d.stage === "验收未通过"} /> : null}
|
|
199
207
|
{d.hasPrototype ? <ReviewButton theme={theme} repo={d.sourceRepo ?? d.repo} featureDir={d.featureDir} title={d.title} waiting={d.attention === "approval"} /> : null}
|
|
200
208
|
{actions.map((a) => (
|
|
201
209
|
<Button key={a} theme={theme} primary={a !== "stop" && !(a === "approve" && d.hasPrototype)} label={ACTION_TEXT[a].label} onPress={() => setAction(a)} />
|
|
@@ -10,3 +10,13 @@ export function setScreenOpener(open: Opener): void {
|
|
|
10
10
|
export function openReview(input: { repo: string; featureDir: string; title: string }): void {
|
|
11
11
|
opener?.({ screenId: "review", params: { repo: input.repo, featureDir: input.featureDir, title: input.title } });
|
|
12
12
|
}
|
|
13
|
+
|
|
14
|
+
/** 1.14: the acceptance checklist of a requirement (the same screen, another page). */
|
|
15
|
+
export function openChecklist(input: { repo: string; featureDir: string; title: string }): void {
|
|
16
|
+
opener?.({ screenId: "review", params: { repo: input.repo, featureDir: input.featureDir, title: input.title, page: "check" } });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** 1.14: the code review page of a requirement (the same screen, another page). */
|
|
20
|
+
export function openCodeReview(input: { repo: string; featureDir: string; title: string }): void {
|
|
21
|
+
opener?.({ screenId: "review", params: { repo: input.repo, featureDir: input.featureDir, title: input.title, page: "code" } });
|
|
22
|
+
}
|
|
@@ -4,12 +4,22 @@ import type { PluginHostProps, PluginScreenProps } from "@getpaseo/plugin/client
|
|
|
4
4
|
import { ExternalLink } from "@getpaseo/plugin/client/ui";
|
|
5
5
|
import { Text, View } from "react-native";
|
|
6
6
|
import { useReviewUrl } from "./data";
|
|
7
|
-
import { openReview } from "./nav";
|
|
7
|
+
import { openChecklist, openCodeReview, openReview } from "./nav";
|
|
8
8
|
import { Button, Status, useStyles } from "./ui";
|
|
9
9
|
import { canEmbedPages, embeddedPage, reviewUnreachable } from "./web";
|
|
10
10
|
|
|
11
11
|
type Theme = PluginHostProps["theme"];
|
|
12
12
|
|
|
13
|
+
/** 1.14: the acceptance checklist, primary while the requirement waits for acceptance. */
|
|
14
|
+
export function ChecklistButton({ theme, repo, featureDir, title, primary }: { theme: Theme; repo: string; featureDir: string; title: string; primary: boolean }) {
|
|
15
|
+
return <Button theme={theme} primary={primary} label="验收清单(可一键试用)" onPress={() => openChecklist({ repo, featureDir, title })} />;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** 1.14: the code review page: AI findings next to the code, the person's comments, pass or send back. */
|
|
19
|
+
export function CodeReviewButton({ theme, repo, featureDir, title, primary, label }: { theme: Theme; repo: string; featureDir: string; title: string; primary: boolean; label: string }) {
|
|
20
|
+
return <Button theme={theme} primary={primary} label={label} onPress={() => openCodeReview({ repo, featureDir, title })} />;
|
|
21
|
+
}
|
|
22
|
+
|
|
13
23
|
/** The button on a requirement with a prototype: primary while the design waits for confirmation. */
|
|
14
24
|
export function ReviewButton({ theme, repo, featureDir, title, waiting }: { theme: Theme; repo: string; featureDir: string; title: string; waiting: boolean }) {
|
|
15
25
|
return <Button theme={theme} primary={waiting} label={waiting ? "审查原型并确认方案" : "审查原型"} onPress={() => openReview({ repo, featureDir, title })} />;
|
|
@@ -19,7 +29,8 @@ export function ReviewScreen({ theme, layout, params }: PluginScreenProps) {
|
|
|
19
29
|
const s = useStyles(theme, layout.compact);
|
|
20
30
|
const repo = params.repo ?? "";
|
|
21
31
|
const featureDir = params.featureDir ?? "";
|
|
22
|
-
const
|
|
32
|
+
const page = params.page === "check" ? "check" : params.page === "code" ? "code" : "prototype";
|
|
33
|
+
const review = useReviewUrl(repo, featureDir, true, page);
|
|
23
34
|
if (!review.data) {
|
|
24
35
|
return (
|
|
25
36
|
<View style={[s.screen, { padding: 16 }]}>
|
|
@@ -38,17 +49,30 @@ export function ReviewScreen({ theme, layout, params }: PluginScreenProps) {
|
|
|
38
49
|
</View>
|
|
39
50
|
);
|
|
40
51
|
}
|
|
41
|
-
const
|
|
52
|
+
const name = page === "check" ? "验收清单" : page === "code" ? "代码审查" : "原型审查";
|
|
53
|
+
const frame = canEmbedPages ? embeddedPage(url, `${name}:${params.title ?? featureDir}`) : null;
|
|
42
54
|
return (
|
|
43
55
|
<View style={[s.screen, { gap: 6 }]}>
|
|
44
56
|
<View style={[s.row, { paddingHorizontal: 12, paddingTop: 8, justifyContent: "space-between" }]}>
|
|
45
|
-
<Text style={s.muted}>
|
|
57
|
+
<Text style={s.muted}>
|
|
58
|
+
{page === "code"
|
|
59
|
+
? readOnly
|
|
60
|
+
? "插件设为只读:可以看改动和 AI 意见,不能提交评审。"
|
|
61
|
+
: "点代码行写意见,AI 的意见标要改或不用改;看完在底部退回修改或评审通过。"
|
|
62
|
+
: page === "check"
|
|
63
|
+
? readOnly
|
|
64
|
+
? "插件设为只读:可以看清单,不能提交。"
|
|
65
|
+
: "逐条核对:通过、不对(写下看到了什么)、先不验;需要时点右上角把它跑起来。"
|
|
66
|
+
: readOnly
|
|
67
|
+
? "插件设为只读:可以看原型,不能提交意见或确认。"
|
|
68
|
+
: "点原型上要改的地方提意见,双击文字直接改字;看完在右下角提交或确认方案。"}
|
|
69
|
+
</Text>
|
|
46
70
|
<ExternalLink href={url}>在新窗口打开</ExternalLink>
|
|
47
71
|
</View>
|
|
48
72
|
{frame ?? (
|
|
49
73
|
<View style={[s.card, { margin: 12, gap: 8 }]}>
|
|
50
|
-
<Text style={s.text}
|
|
51
|
-
<ExternalLink href={url}
|
|
74
|
+
<Text style={s.text}>{page === "prototype" ? "原型审查页" : name}在浏览器中打开效果最好(手机上屏幕较小,建议用电脑)。</Text>
|
|
75
|
+
<ExternalLink href={url}>{page === "prototype" ? "打开原型审查页" : `打开${name}`}</ExternalLink>
|
|
52
76
|
</View>
|
|
53
77
|
)}
|
|
54
78
|
</View>
|
|
@@ -46,10 +46,10 @@ export function createHandlers(input: { req: ReqRunner; kitHome: () => string; r
|
|
|
46
46
|
const result = (await req(["run", action, featureDir, "--repo", repo, ...extra], { timeoutMs: 200_000 })) as { ok: boolean; message: string };
|
|
47
47
|
return { ok: result.ok, message: result.message };
|
|
48
48
|
},
|
|
49
|
-
async reviewUrl({ repo, featureDir }) {
|
|
49
|
+
async reviewUrl({ repo, featureDir, page }) {
|
|
50
50
|
// Read-only plugin: the page opens without the buttons that send or confirm.
|
|
51
51
|
const ro = await input.readOnly();
|
|
52
|
-
return (await req(["review-url", featureDir, "--repo", repo, ...(ro ? ["--read-only"] : [])], { timeoutMs: 30_000 })) as { url: string; readOnly: boolean };
|
|
52
|
+
return (await req(["review-url", featureDir, "--repo", repo, ...(page === "check" || page === "code" ? ["--page", page] : []), ...(ro ? ["--read-only"] : [])], { timeoutMs: 30_000 })) as { url: string; readOnly: boolean };
|
|
53
53
|
},
|
|
54
54
|
async config({ repo }) {
|
|
55
55
|
return (await req(repo ? ["config", "--repo", repo] : ["config", "--machine"])) as ConfigViewData;
|
|
@@ -78,6 +78,8 @@ export interface GateRun {
|
|
|
78
78
|
|
|
79
79
|
export interface RequirementDetail extends Requirement {
|
|
80
80
|
files: string[];
|
|
81
|
+
/** req-kit 1.14+: the person's code review on the code review page; null before one. */
|
|
82
|
+
humanReview?: { status: "approved" | "changes_requested"; reviewer: string; at: string; reviewers: string[]; items: number; stale: boolean; consumedAttempt: number | null } | null;
|
|
81
83
|
/** req-kit 1.13+: what to expect, from this machine's completed requirements of the same profile; null without enough history. */
|
|
82
84
|
estimate?: {
|
|
83
85
|
basis: number;
|
|
@@ -191,7 +193,8 @@ export const runRpc = defineRpc({
|
|
|
191
193
|
/** req api review-url (1.13): the address of the requirement's prototype review page. */
|
|
192
194
|
export const reviewUrlRpc = defineRpc({
|
|
193
195
|
name: "req.review-url",
|
|
194
|
-
|
|
196
|
+
/** page (1.14): "check" for the acceptance checklist, "code" for the code review page; the prototype review page otherwise. */
|
|
197
|
+
input: target.extend({ page: z.enum(["prototype", "check", "code"]).optional() }),
|
|
195
198
|
output: z.object({ url: z.string(), readOnly: z.boolean() }),
|
|
196
199
|
});
|
|
197
200
|
|
package/site-dist/404.html
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
<link rel="preload stylesheet" href="/assets/style.Dwsa4SMe.css" as="style">
|
|
10
10
|
<link rel="preload stylesheet" href="/vp-icons.css" as="style">
|
|
11
11
|
|
|
12
|
-
<script type="module" src="/assets/app.
|
|
12
|
+
<script type="module" src="/assets/app.Cp7WwPWE.js"></script>
|
|
13
13
|
<link rel="preload" href="/assets/inter-roman-latin.Di8DUHzh.woff2" as="font" type="font/woff2" crossorigin="">
|
|
14
14
|
<link rel="icon" type="image/svg+xml" href="/logo.svg">
|
|
15
15
|
<meta name="theme-color" content="#3f6fd8">
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
</head>
|
|
22
22
|
<body>
|
|
23
23
|
<div id="app"></div>
|
|
24
|
-
<script>window.__VP_HASH_MAP__=JSON.parse("{\"changelog.md\":\"
|
|
24
|
+
<script>window.__VP_HASH_MAP__=JSON.parse("{\"changelog.md\":\"Zi-_3Rjh\",\"guide_getting-started.md\":\"D8XlIQOg\",\"guide_how-it-works.md\":\"D-kQsF1t\",\"guide_index.md\":\"CLnGCmGW\",\"guide_install.md\":\"DkCyZsOU\",\"guide_maintainer.md\":\"C1v7EpSY\",\"guide_phases.md\":\"BpGAb1re\",\"guide_project.md\":\"Csi5wiyt\",\"guide_user.md\":\"B4N9ywmr\",\"index.md\":\"Cf_sSf_f\",\"reference_adr_0001-step-agents-under-origin-chat.md\":\"RtRTq7oi\",\"reference_adr_0002-no-directory-switch-in-chat.md\":\"BxtN-Aa2\",\"reference_adr_0003-script-engine-not-hub.md\":\"66EgZtqx\",\"reference_adr_0004-result-files.md\":\"DbqcCozI\",\"reference_adr_0005-no-default-branch-local-commits-only.md\":\"CkImA4s6\",\"reference_adr_0006-skill-install-targets.md\":\"CwY8YepV\",\"reference_adr_0007-own-interview-skill.md\":\"CryhxRyG\",\"reference_adr_0008-entries-do-not-call-entries.md\":\"DsRWAgeA\",\"reference_adr_0009-versioned-install-layout.md\":\"CSg1VqlW\",\"reference_adr_0010-generated-feature-overview.md\":\"D8F3MK-h\",\"reference_adr_0011-resume-after-completion.md\":\"BCrZrKjf\",\"reference_adr_0012-review-panel.md\":\"k9rrHFFc\",\"reference_adr_0013-no-release-tooling.md\":\"BQeJ8dEI\",\"reference_adr_0014-notifier-slot.md\":\"C23QR1xO\",\"reference_adr_0015-incremental-continue.md\":\"Buy7wwO8\",\"reference_adr_0016-upgrade-from-tags.md\":\"qfIW-tqJ\",\"reference_adr_0017-use-paseo-capabilities.md\":\"CjcGli87\",\"reference_adr_0018-own-worktree-option.md\":\"fVDpgGSE\",\"reference_adr_0019-parallel-orchestration.md\":\"DrfrV0To\",\"reference_adr_0020-scheduling-through-the-chat.md\":\"BqSemGFG\",\"reference_adr_0021-npm-version-manager.md\":\"CU3PG6y5\",\"reference_adr_0022-develop-and-verify-phases.md\":\"C6fQDLq4\",\"reference_adr_0023-types-gate-slices.md\":\"D2E6yyYQ\",\"reference_adr_0024-confirm-early-review-axes.md\":\"FmK_ke0x\",\"reference_adr_0025-joint-requirements.md\":\"ChoKvMUx\",\"reference_adr_0026-machine-records-and-api.md\":\"veybjBPq\",\"reference_adr_0027-paseo-plugin.md\":\"BMX33ZNU\",\"reference_adr_0028-external-integrations.md\":\"DtdMe8RK\",\"reference_adr_0029-requirements-from-cards.md\":\"BHd1SQPe\",\"reference_adr_0030-card-intake-process.md\":\"kLc-DG9_\",\"reference_adr_0031-fewer-interventions.md\":\"BDnQcz8g\",\"reference_adr_0032-npm-only-install.md\":\"C-3L1FkX\",\"reference_adr_0033-prototype-review-and-ui-check.md\":\"hFcQzmaa\",\"reference_adr_index.md\":\"BYF-Ofor\",\"reference_commands.md\":\"ClKbS-t8\",\"reference_config.md\":\"CRe27LaA\",\"reference_contracts_cli-json.md\":\"CCvKLasM\",\"reference_contracts_feature-files.md\":\"DSiA8OEH\",\"reference_contracts_index.md\":\"Bkznz4px\",\"reference_contracts_notifier-event.md\":\"CCfrkm_A\",\"reference_contracts_result-file.md\":\"DmR4fjB-\",\"reference_contracts_spec.md\":\"BwwS0L_j\",\"reference_contracts_state.md\":\"EEBd_l2P\",\"reference_contracts_usage-record.md\":\"D5krNLW7\",\"reference_contracts_verify.md\":\"CbA88u6S\",\"reference_scope.md\":\"Kzqw8U_f\"}");window.__VP_SITE_DATA__=JSON.parse("{\"lang\":\"zh-CN\",\"dir\":\"ltr\",\"title\":\"req-kit\",\"titleTemplate\":\":title · req-kit\",\"description\":\"在 Paseo 中用对话对齐需求,由一组 AI agent 在后台完成设计、实现、评审和验证。给测试、产品、项目经理用的需求开发体系。\",\"base\":\"/\",\"head\":[],\"router\":{\"prefetchLinks\":true},\"appearance\":true,\"themeConfig\":{\"logo\":\"/logo.svg\",\"siteTitle\":\"req-kit\",\"nav\":[{\"text\":\"指南\",\"link\":\"/guide/\",\"activeMatch\":\"^/guide/\"},{\"text\":\"参考\",\"link\":\"/reference/commands\",\"activeMatch\":\"^/reference/\"},{\"text\":\"更新记录\",\"link\":\"/changelog\"},{\"text\":\"v1.14.0-beta.4\",\"items\":[{\"text\":\"更新记录\",\"link\":\"/changelog\"},{\"text\":\"GitHub Releases\",\"link\":\"https://github.com/yiyisf/paseo-req-kit/releases\"},{\"text\":\"范围与取舍\",\"link\":\"/reference/scope\"}]}],\"sidebar\":{\"/guide/\":[{\"text\":\"开始\",\"items\":[{\"text\":\"req-kit 是什么\",\"link\":\"/guide/\"},{\"text\":\"快速开始\",\"link\":\"/guide/getting-started\"},{\"text\":\"工作原理\",\"link\":\"/guide/how-it-works\"}]},{\"text\":\"使用\",\"items\":[{\"text\":\"使用者指南\",\"link\":\"/guide/user\"},{\"text\":\"分阶段执行与提测\",\"link\":\"/guide/phases\"}]},{\"text\":\"安装与维护\",\"items\":[{\"text\":\"安装与升级\",\"link\":\"/guide/install\"},{\"text\":\"项目定制与运行规则\",\"link\":\"/guide/project\"},{\"text\":\"维护者指南\",\"link\":\"/guide/maintainer\"}]}],\"/reference/\":[{\"text\":\"参考\",\"items\":[{\"text\":\"命令\",\"link\":\"/reference/commands\"},{\"text\":\"配置项\",\"link\":\"/reference/config\"},{\"text\":\"范围与取舍\",\"link\":\"/reference/scope\"}]},{\"text\":\"文件格式约定\",\"collapsed\":false,\"items\":[{\"text\":\"总览\",\"link\":\"/reference/contracts/\"},{\"text\":\"需求说明 spec.md\",\"link\":\"/reference/contracts/spec\"},{\"text\":\"状态文件 state.json\",\"link\":\"/reference/contracts/state\"},{\"text\":\"结果文件\",\"link\":\"/reference/contracts/result-file\"},{\"text\":\"功能目录中的文件\",\"link\":\"/reference/contracts/feature-files\"},{\"text\":\"验证方法 verify.md\",\"link\":\"/reference/contracts/verify\"},{\"text\":\"外部通知事件\",\"link\":\"/reference/contracts/notifier-event\"},{\"text\":\"req api 的 JSON 输出\",\"link\":\"/reference/contracts/cli-json\"},{\"text\":\"需求索引与使用记录\",\"link\":\"/reference/contracts/usage-record\"}]},{\"text\":\"设计决定(ADR)\",\"collapsed\":true,\"items\":[{\"text\":\"总览\",\"link\":\"/reference/adr/\"},{\"text\":\"0001 步骤 agent 挂在发起的对话下\",\"link\":\"/reference/adr/0001-step-agents-under-origin-chat\"},{\"text\":\"0002 对话过程中不切换工作目录\",\"link\":\"/reference/adr/0002-no-directory-switch-in-chat\"},{\"text\":\"0003 第 3–11 步由脚本引擎驱动,不用 Hub 多步骤工作流\",\"link\":\"/reference/adr/0003-script-engine-not-hub\"},{\"text\":\"0004 步骤结论只看结果文件\",\"link\":\"/reference/adr/0004-result-files\"},{\"text\":\"0005 不在默认分支上运行,只做本地提交\",\"link\":\"/reference/adr/0005-no-default-branch-local-commits-only\"},{\"text\":\"0006 只安装到 ~/.agents/skills 和 ~/.claude/skills\",\"link\":\"/reference/adr/0006-skill-install-targets\"},{\"text\":\"0007 自带需求访谈 skill,不依赖外部 skill 包\",\"link\":\"/reference/adr/0007-own-interview-skill\"},{\"text\":\"0008 入口 skill 之间不互相调用\",\"link\":\"/reference/adr/0008-entries-do-not-call-entries\"},{\"text\":\"0009 程序与本机配置分开存放,按版本目录并存\",\"link\":\"/reference/adr/0009-versioned-install-layout\"},{\"text\":\"0010 需求总览现场生成,不维护 INDEX.md\",\"link\":\"/reference/adr/0010-generated-feature-overview\"},{\"text\":\"0011 完成后修改 spec 可以继续开发\",\"link\":\"/reference/adr/0011-resume-after-completion\"},{\"text\":\"0012 评审小组:首轮并行评审,评审者核实汇总\",\"link\":\"/reference/adr/0012-review-panel\"},{\"text\":\"0013 发布不使用 changesets 等工具\",\"link\":\"/reference/adr/0013-no-release-tooling\"},{\"text\":\"0014 外部通知只留接口,不内置任何 IM\",\"link\":\"/reference/adr/0014-notifier-slot\"},{\"text\":\"0015 需求变更后增量继续,而不是从零重做\",\"link\":\"/reference/adr/0015-incremental-continue\"},{\"text\":\"0016 一键升级:从 git 标签下载到 versions/ 再切换\",\"link\":\"/reference/adr/0016-upgrade-from-tags\"},{\"text\":\"0017 用好 Paseo 自身的能力:profile、工作区脚本、用量和时间线\",\"link\":\"/reference/adr/0017-use-paseo-capabilities\"},{\"text\":\"0018 可选:流水线为需求新建 worktree 工作区\",\"link\":\"/reference/adr/0018-own-worktree-option\"},{\"text\":\"0019 用 Paseo 的并行子 agent:确认前独立审阅、方案提案\",\"link\":\"/reference/adr/0019-parallel-orchestration\"},{\"text\":\"0020 调度:用 Paseo 定时任务把消息发回对话,排队在本机进行\",\"link\":\"/reference/adr/0020-scheduling-through-the-chat\"},{\"text\":\"0021 npm 分发:全局包是版本管理器,各版本仍装在 versions/ 下\",\"link\":\"/reference/adr/0021-npm-version-manager\"},{\"text\":\"0022 分阶段执行:研发阶段、闸口、测试验证阶段\",\"link\":\"/reference/adr/0022-develop-and-verify-phases\"},{\"text\":\"0023 需求类型、质量门、按切片实现\",\"link\":\"/reference/adr/0023-types-gate-slices\"},{\"text\":\"0024 让分歧更早暴露:验收例子、方案确认点、两个方向的评审、反复的问题变成规则\",\"link\":\"/reference/adr/0024-confirm-early-review-axes\"},{\"text\":\"0025 联合需求:一个需求同时改几个仓库\",\"link\":\"/reference/adr/0025-joint-requirements\"},{\"text\":\"0026 本机的需求索引、使用记录和 req api\",\"link\":\"/reference/adr/0026-machine-records-and-api\"},{\"text\":\"0027 Paseo 插件:随 req-kit 一起发布,只通过 req api 工作\",\"link\":\"/reference/adr/0027-paseo-plugin\"},{\"text\":\"0028 外部系统对接:命令写法在配置里,内容由 AI 整理,调用是确定的\",\"link\":\"/reference/adr/0028-external-integrations\"},{\"text\":\"0029 从卡片开始的需求:需求会话代替人,卡片描述代替对话\",\"link\":\"/reference/adr/0029-requirements-from-cards\"},{\"text\":\"0030 接单器:共享机器上定时接卡片、转达回复\",\"link\":\"/reference/adr/0030-card-intake-process\"},{\"text\":\"0031 少等人:每种中断都有自动的下一步\",\"link\":\"/reference/adr/0031-fewer-interventions\"},{\"text\":\"0032 安装、升级和文档只依赖 npm 源\",\"link\":\"/reference/adr/0032-npm-only-install\"},{\"text\":\"0033 原型审查页和不靠看图的界面检查\",\"link\":\"/reference/adr/0033-prototype-review-and-ui-check\"}]}]},\"socialLinks\":[{\"icon\":\"github\",\"link\":\"https://github.com/yiyisf/paseo-req-kit\"}],\"editLink\":{\"pattern\":\"https://github.com/yiyisf/paseo-req-kit/edit/main/site/:path\",\"text\":\"在 GitHub 上编辑此页\"},\"footer\":{\"message\":\"以 MIT 许可发布。req-kit 是运行在 Paseo 上的社区项目。\",\"copyright\":\"Copyright © 2026 yiyisf\"},\"search\":{\"provider\":\"local\",\"options\":{\"translations\":{\"button\":{\"buttonText\":\"搜索文档\",\"buttonAriaLabel\":\"搜索文档\"},\"modal\":{\"noResultsText\":\"没有找到相关结果\",\"resetButtonTitle\":\"清除\",\"footer\":{\"selectText\":\"选择\",\"navigateText\":\"切换\",\"closeText\":\"关闭\"}}}}},\"outline\":{\"level\":[2,3],\"label\":\"本页内容\"},\"docFooter\":{\"prev\":\"上一页\",\"next\":\"下一页\"},\"lastUpdated\":{\"text\":\"最后更新\"},\"returnToTopLabel\":\"回到顶部\",\"sidebarMenuLabel\":\"目录\",\"darkModeSwitchLabel\":\"外观\",\"lightModeSwitchTitle\":\"切换到浅色\",\"darkModeSwitchTitle\":\"切换到深色\",\"externalLinkIcon\":true,\"notFound\":{\"title\":\"页面不存在\",\"quote\":\"这个页面可能已经移动或删除。\",\"linkText\":\"回到首页\"}},\"locales\":{},\"scrollOffset\":134,\"cleanUrls\":true}");</script>
|
|
25
25
|
|
|
26
26
|
</body>
|
|
27
27
|
</html>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{R as p}from"./chunks/theme.
|
|
1
|
+
import{R as p}from"./chunks/theme.DvVUfKr3.js";import{R as s,a4 as i,a5 as u,a6 as c,a7 as l,a8 as f,a9 as d,aa as m,ab as h,ac as g,ad as A,d as v,u as R,v as w,s as y,ae as C,af as P,ag as b,a2 as E}from"./chunks/framework.B9qR_F1m.js";function r(e){if(e.extends){const a=r(e.extends);return{...a,...e,async enhanceApp(t){a.enhanceApp&&await a.enhanceApp(t),e.enhanceApp&&await e.enhanceApp(t)}}}return e}const n=r(p),S=v({name:"VitePressApp",setup(){const{site:e,lang:a,dir:t}=R();return w(()=>{y(()=>{document.documentElement.lang=a.value,document.documentElement.dir=t.value})}),e.value.router.prefetchLinks&&C(),P(),b(),n.setup&&n.setup(),()=>E(n.Layout)}});async function T(){globalThis.__VITEPRESS__=!0;const e=_(),a=D();a.provide(u,e);const t=c(e.route);return a.provide(l,t),a.component("Content",f),a.component("ClientOnly",d),Object.defineProperties(a.config.globalProperties,{$frontmatter:{get(){return t.frontmatter.value}},$params:{get(){return t.page.value.params}}}),n.enhanceApp&&await n.enhanceApp({app:a,router:e,siteData:m}),{app:a,router:e,data:t}}function D(){return A(S)}function _(){let e=s;return h(a=>{let t=g(a),o=null;return t&&(e&&(t=t.replace(/\.js$/,".lean.js")),o=import(t)),s&&(e=!1),o},n.NotFound)}s&&T().then(({app:e,router:a,data:t})=>{a.go().then(()=>{i(a.route,t.site),e.mount("#app")})});export{T as createApp};
|