ai-project-manage-cli 2.0.4 → 2.0.6
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/dist/api/cli.d.ts +16 -6
- package/dist/api/cli.d.ts.map +1 -1
- package/dist/api/client.d.ts +1 -0
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/request-config.d.ts +2 -1
- package/dist/api/request-config.d.ts.map +1 -1
- package/dist/api/request-config.js +4 -0
- package/dist/api/request-config.js.map +1 -1
- package/dist/cli/commands/get.d.ts.map +1 -1
- package/dist/cli/commands/get.js +24 -1
- package/dist/cli/commands/get.js.map +1 -1
- package/dist/cli/commands/set.d.ts.map +1 -1
- package/dist/cli/commands/set.js +18 -0
- package/dist/cli/commands/set.js.map +1 -1
- package/package.json +1 -1
- package/templates/skills/prd-review/SKILL.md +48 -0
- package/templates/skills/{requirement-review → prd-review}/output-template.md +0 -2
- package/templates/skills/prd-sync/SKILL.md +63 -0
- package/templates/skills/requirement-doc-refine/SKILL.md +0 -94
- package/templates/skills/requirement-review/SKILL.md +0 -45
- /package/templates/skills/{requirement-doc-refine → prd-sync}/updated-requirement-template.md +0 -0
package/dist/api/cli.d.ts
CHANGED
|
@@ -32,13 +32,13 @@ export type ReqCliResourceRequirementGet = {
|
|
|
32
32
|
id: number;
|
|
33
33
|
};
|
|
34
34
|
export type ResCliResourceRequirementGet = {
|
|
35
|
-
id: number;
|
|
36
|
-
spaceId: string;
|
|
37
|
-
workitemId: string;
|
|
38
35
|
content: string;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
comments: {
|
|
37
|
+
reviewer: number;
|
|
38
|
+
content: string;
|
|
39
|
+
reply: string;
|
|
40
|
+
employeeId: number;
|
|
41
|
+
}[];
|
|
42
42
|
};
|
|
43
43
|
/** 对应 lpm-server `POST /cli/resource/requirement/comment`(更新 RequirementDeveloper) */
|
|
44
44
|
export type ReqCliResourceRequirementCommentSet = {
|
|
@@ -56,4 +56,14 @@ export type ResCliResourceRequirementCommentSet = {
|
|
|
56
56
|
reviewReply: string | null;
|
|
57
57
|
branchName: string | null;
|
|
58
58
|
};
|
|
59
|
+
/** 对应 lpm-server `POST /cli/resource/requirement/content`(同步需求正文) */
|
|
60
|
+
export type ReqCliResourceRequirementContentSet = {
|
|
61
|
+
id: number;
|
|
62
|
+
content: string;
|
|
63
|
+
};
|
|
64
|
+
export type ResCliResourceRequirementContentSet = {
|
|
65
|
+
id: number;
|
|
66
|
+
workitemId: string;
|
|
67
|
+
clearedRequirementDevelopers: number;
|
|
68
|
+
};
|
|
59
69
|
//# sourceMappingURL=cli.d.ts.map
|
package/dist/api/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/api/cli.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAEtC,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE;QACN,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,OAAO,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,MAAM,EAAE;QACN,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;CACH,CAAC;AAEF,gEAAgE;AAChE,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,qDAAqD;AACrD,MAAM,MAAM,4BAA4B,GAAG;IACzC,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/api/cli.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAEtC,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE;QACN,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,OAAO,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,MAAM,EAAE;QACN,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;CACH,CAAC;AAEF,gEAAgE;AAChE,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,qDAAqD;AACrD,MAAM,MAAM,4BAA4B,GAAG;IACzC,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE;QACR,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;KACpB,EAAE,CAAC;CACL,CAAC;AAEF,sFAAsF;AACtF,MAAM,MAAM,mCAAmC,GAAG;IAChD,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,mCAAmC,GAAG;IAChD,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC;AAEF,qEAAqE;AACrE,MAAM,MAAM,mCAAmC,GAAG;IAChD,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,mCAAmC,GAAG;IAChD,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,4BAA4B,EAAE,MAAM,CAAC;CACtC,CAAC"}
|
package/dist/api/client.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ declare const api: ApiClient<{
|
|
|
13
13
|
readonly variablesGet: import("listpage-http").EndpointConfig<void, import("./cli").ResCliVariables>;
|
|
14
14
|
readonly resourceRequirementGet: import("listpage-http").EndpointConfig<import("./cli").ReqCliResourceRequirementGet, import("./cli").ResCliResourceRequirementGet>;
|
|
15
15
|
readonly resourceRequirementCommentSet: import("listpage-http").EndpointConfig<import("./cli").ReqCliResourceRequirementCommentSet, import("./cli").ResCliResourceRequirementCommentSet>;
|
|
16
|
+
readonly resourceRequirementContentSet: import("listpage-http").EndpointConfig<import("./cli").ReqCliResourceRequirementContentSet, import("./cli").ResCliResourceRequirementContentSet>;
|
|
16
17
|
};
|
|
17
18
|
readonly employee: {
|
|
18
19
|
readonly auth: import("listpage-http").EndpointConfig<import("./cli").ReqCliLoginByEmployee, import("./cli").ResCliLoginByEmployee>;
|
package/dist/api/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGjD,MAAM,MAAM,sBAAsB,GAAG;IACnC,kBAAkB;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAMF,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,aAAa,CAAC,CAAC;AAExD,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,SAAS,CAkB1E;AAID,QAAA,MAAM,GAAG
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGjD,MAAM,MAAM,sBAAsB,GAAG;IACnC,kBAAkB;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAMF,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,aAAa,CAAC,CAAC;AAExD,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,SAAS,CAkB1E;AAID,QAAA,MAAM,GAAG;;;;;;;;;;EAGP,CAAC;AAEH,eAAe,GAAG,CAAC"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type { ReqCliLoginByEmployee, ReqCliResourceRequirementCommentSet, ReqCliResourceRequirementGet, ResCliLoginByEmployee, ResCliResourceRequirementCommentSet, ResCliResourceRequirementGet, ResCliVariables } from "./cli";
|
|
1
|
+
import type { ReqCliLoginByEmployee, ReqCliResourceRequirementCommentSet, ReqCliResourceRequirementContentSet, ReqCliResourceRequirementGet, ResCliLoginByEmployee, ResCliResourceRequirementCommentSet, ResCliResourceRequirementContentSet, ResCliResourceRequirementGet, ResCliVariables } from "./cli";
|
|
2
2
|
export declare const requestConfig: {
|
|
3
3
|
readonly cli: {
|
|
4
4
|
readonly variablesGet: import("listpage-http").EndpointConfig<void, ResCliVariables>;
|
|
5
5
|
readonly resourceRequirementGet: import("listpage-http").EndpointConfig<ReqCliResourceRequirementGet, ResCliResourceRequirementGet>;
|
|
6
6
|
readonly resourceRequirementCommentSet: import("listpage-http").EndpointConfig<ReqCliResourceRequirementCommentSet, ResCliResourceRequirementCommentSet>;
|
|
7
|
+
readonly resourceRequirementContentSet: import("listpage-http").EndpointConfig<ReqCliResourceRequirementContentSet, ResCliResourceRequirementContentSet>;
|
|
7
8
|
};
|
|
8
9
|
readonly employee: {
|
|
9
10
|
/** 登录等鉴权(路径落在 `/cli/employee/auth`,便于与业务接口区分) */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-config.d.ts","sourceRoot":"","sources":["../../src/api/request-config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,qBAAqB,EACrB,mCAAmC,EACnC,4BAA4B,EAE5B,qBAAqB,EACrB,mCAAmC,EACnC,4BAA4B,EAC5B,eAAe,EAChB,MAAM,OAAO,CAAC;AAEf,eAAO,MAAM,aAAa
|
|
1
|
+
{"version":3,"file":"request-config.d.ts","sourceRoot":"","sources":["../../src/api/request-config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,qBAAqB,EACrB,mCAAmC,EACnC,mCAAmC,EACnC,4BAA4B,EAE5B,qBAAqB,EACrB,mCAAmC,EACnC,mCAAmC,EACnC,4BAA4B,EAC5B,eAAe,EAChB,MAAM,OAAO,CAAC;AAEf,eAAO,MAAM,aAAa;;;;;;;;QA6BtB,iDAAiD;;;CAO3C,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,OAAO,aAAa,CAAC"}
|
|
@@ -16,6 +16,10 @@ exports.requestConfig = {
|
|
|
16
16
|
method: "POST",
|
|
17
17
|
path: "/cli/resource/requirement/comment",
|
|
18
18
|
}),
|
|
19
|
+
resourceRequirementContentSet: (0, listpage_http_1.defineEndpoint)({
|
|
20
|
+
method: "POST",
|
|
21
|
+
path: "/cli/resource/requirement/content",
|
|
22
|
+
}),
|
|
19
23
|
},
|
|
20
24
|
employee: {
|
|
21
25
|
/** 登录等鉴权(路径落在 `/cli/employee/auth`,便于与业务接口区分) */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-config.js","sourceRoot":"","sources":["../../src/api/request-config.ts"],"names":[],"mappings":";;;AAAA,iDAA+C;
|
|
1
|
+
{"version":3,"file":"request-config.js","sourceRoot":"","sources":["../../src/api/request-config.ts"],"names":[],"mappings":";;;AAAA,iDAA+C;AAelC,QAAA,aAAa,GAAG;IAC3B,GAAG,EAAE;QACH,YAAY,EAAE,IAAA,8BAAc,EAAsC;YAChE,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,oBAAoB;SAC3B,CAAC;QACF,sBAAsB,EAAE,IAAA,8BAAc,EAGpC;YACA,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,2BAA2B;SAClC,CAAC;QACF,6BAA6B,EAAE,IAAA,8BAAc,EAG3C;YACA,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,mCAAmC;SAC1C,CAAC;QACF,6BAA6B,EAAE,IAAA,8BAAc,EAG3C;YACA,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,mCAAmC;SAC1C,CAAC;KACH;IACD,QAAQ,EAAE;QACR,iDAAiD;QACjD,IAAI,EAAE,IAAA,8BAAc,EAA+C;YACjE,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,oBAAoB;YAC1B,YAAY,EAAE,KAAK;SACpB,CAAC;KACH;CACO,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/get.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"get.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/get.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAazC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgD1D"}
|
package/dist/cli/commands/get.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.registerGetCommands = registerGetCommands;
|
|
|
7
7
|
const node_fs_1 = require("node:fs");
|
|
8
8
|
const client_1 = __importDefault(require("../../api/client"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const apm_config_1 = require("../../core/apm-config");
|
|
10
11
|
function parseRequirementId(value) {
|
|
11
12
|
const n = Number.parseInt(value, 10);
|
|
12
13
|
if (!Number.isFinite(n) || n < 1) {
|
|
@@ -20,13 +21,35 @@ function registerGetCommands(program) {
|
|
|
20
21
|
.command("requirement")
|
|
21
22
|
.description("按需求 ID 获取需求文档正文")
|
|
22
23
|
.argument("<id>", "需求 ID", parseRequirementId)
|
|
23
|
-
.
|
|
24
|
+
.option("--all", "包含该需求下的所有评论")
|
|
25
|
+
.action(async (id, opts) => {
|
|
24
26
|
const res = await client_1.default.cli.resourceRequirementGet({ id });
|
|
25
27
|
if (!res.content) {
|
|
26
28
|
throw new Error("需求文档内容为空");
|
|
27
29
|
}
|
|
28
30
|
const prdPath = path_1.default.join(".apm/workitems", String(id), "prd.md");
|
|
31
|
+
(0, node_fs_1.mkdirSync)(path_1.default.dirname(prdPath), { recursive: true });
|
|
29
32
|
(0, node_fs_1.writeFileSync)(prdPath, res.content);
|
|
33
|
+
const config = (0, apm_config_1.loadApmConfig)();
|
|
34
|
+
const currentComment = res.comments.find((c) => c.employeeId === config.auth.id);
|
|
35
|
+
if (currentComment) {
|
|
36
|
+
(0, node_fs_1.writeFileSync)(path_1.default.join(".apm/workitems", String(id), "comment.md"), `======当前评论内容======
|
|
37
|
+
${currentComment.content}
|
|
38
|
+
=========当前评论回复==========
|
|
39
|
+
${currentComment.reply || ""}
|
|
40
|
+
`);
|
|
41
|
+
}
|
|
42
|
+
if (opts.all) {
|
|
43
|
+
const text = `======需求内容======
|
|
44
|
+
${res.content}
|
|
45
|
+
=========评论内容==========
|
|
46
|
+
${res.comments
|
|
47
|
+
.map((c) => `### ${c.reviewer}\n#### 内容\n${c.content}\n#### 回复\n${c.reply}`)
|
|
48
|
+
.join("\n\n")}
|
|
49
|
+
`;
|
|
50
|
+
console.log(text);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
30
53
|
console.log(`需求内容如下(已保存到 ${prdPath}):`);
|
|
31
54
|
console.log("--------------------------------");
|
|
32
55
|
console.log(res.content);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get.js","sourceRoot":"","sources":["../../../src/cli/commands/get.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"get.js","sourceRoot":"","sources":["../../../src/cli/commands/get.ts"],"names":[],"mappings":";;;;;AAcA,kDAgDC;AA9DD,qCAAmD;AAEnD,8DAAmC;AACnC,gDAAwB;AACxB,sDAAsD;AAEtD,SAAS,kBAAkB,CAAC,KAAa;IACvC,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACrC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,aAAa,KAAK,EAAE,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAgB,mBAAmB,CAAC,OAAgB;IAClD,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IAE9D,MAAM;SACH,OAAO,CAAC,aAAa,CAAC;SACtB,WAAW,CAAC,iBAAiB,CAAC;SAC9B,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,kBAAkB,CAAC;SAC7C,MAAM,CAAC,OAAO,EAAE,aAAa,CAAC;SAC9B,MAAM,CAAC,KAAK,EAAE,EAAU,EAAE,IAAuB,EAAE,EAAE;QACpD,MAAM,GAAG,GAAG,MAAM,gBAAG,CAAC,GAAG,CAAC,sBAAsB,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACzD,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC;QAC9B,CAAC;QACD,MAAM,OAAO,GAAG,cAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC;QAClE,IAAA,mBAAS,EAAC,cAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,IAAA,uBAAa,EAAC,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;QACpC,MAAM,MAAM,GAAG,IAAA,0BAAa,GAAE,CAAC;QAC/B,MAAM,cAAc,GAAG,GAAG,CAAC,QAAQ,CAAC,IAAI,CACtC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,MAAM,CAAC,IAAI,CAAC,EAAE,CACvC,CAAC;QACF,IAAI,cAAc,EAAE,CAAC;YACnB,IAAA,uBAAa,EACX,cAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE,YAAY,CAAC,EACrD;EACR,cAAc,CAAC,OAAO;;EAEtB,cAAc,CAAC,KAAK,IAAI,EAAE;CAC3B,CACQ,CAAC;QACJ,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,GAAG;EACnB,GAAG,CAAC,OAAO;;EAEX,GAAG,CAAC,QAAQ;iBACX,GAAG,CACF,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,QAAQ,cAAc,CAAC,CAAC,OAAO,cAAc,CAAC,CAAC,KAAK,EAAE,CACvE;iBACA,IAAI,CAAC,MAAM,CAAC;CACd,CAAC;YACM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAClB,OAAO;QACT,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,eAAe,OAAO,IAAI,CAAC,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"set.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/set.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWzC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"set.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/set.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWzC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAwD1D"}
|
package/dist/cli/commands/set.js
CHANGED
|
@@ -16,6 +16,24 @@ function parseRequirementId(value) {
|
|
|
16
16
|
}
|
|
17
17
|
function registerSetCommands(program) {
|
|
18
18
|
const setCmd = program.command("set").description("向服务端提交本地资源");
|
|
19
|
+
setCmd
|
|
20
|
+
.command("requirement")
|
|
21
|
+
.description("读取本地需求正文文件并同步到服务端(默认:.apm/workitems/<需求ID>/prd.md)")
|
|
22
|
+
.argument("<id>", "需求 ID", parseRequirementId)
|
|
23
|
+
.action(async (id) => {
|
|
24
|
+
const filePath = node_path_1.default.join(process.cwd(), ".apm/workitems", String(id), "prd.md");
|
|
25
|
+
if (!(0, node_fs_1.existsSync)(filePath)) {
|
|
26
|
+
console.error(`未找到文件: ${filePath}`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
const content = (0, node_fs_1.readFileSync)(filePath, "utf8");
|
|
30
|
+
if (!content.trim()) {
|
|
31
|
+
console.error("需求正文为空");
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
const res = await client_1.default.cli.resourceRequirementContentSet({ id, content });
|
|
35
|
+
console.log(`需求正文已同步(id=${res.id}`);
|
|
36
|
+
});
|
|
19
37
|
setCmd
|
|
20
38
|
.command("comment")
|
|
21
39
|
.description("读取 .apm/workitems/<需求ID>/comment.md 并提交评审记录(写入服务端需求字段)")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"set.js","sourceRoot":"","sources":["../../../src/cli/commands/set.ts"],"names":[],"mappings":";;;;;AAaA,
|
|
1
|
+
{"version":3,"file":"set.js","sourceRoot":"","sources":["../../../src/cli/commands/set.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: prd-review
|
|
3
|
+
description: 结合本仓库上下文对需求做结构化评审,只有当用户主动提及该技能时才可被使用,该技能调用依赖需求ID。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 需求评审
|
|
7
|
+
|
|
8
|
+
## 触发条件
|
|
9
|
+
|
|
10
|
+
用户**明确声明**使用本技能(例如在对话中 @ 本 SKILL)
|
|
11
|
+
|
|
12
|
+
## 输入
|
|
13
|
+
|
|
14
|
+
- **`requirementId`(必须)**:需求 ID。
|
|
15
|
+
|
|
16
|
+
## 输出
|
|
17
|
+
|
|
18
|
+
- **评审正文(须写入 comment)**:须先按模板在**逻辑上**成文(完整 Markdown 字符串),不得仅给零散要点。**对话中不要粘贴完整评审正文**;全文仅写入 **`.apm/workitems/<requirementId>/comment.md`** 并通过 **`apm set comment`** 提交,供用户在需求侧或系统中查看。
|
|
19
|
+
- 将需求拆成若干条「需求点」(若本就一条则一条),对每条按 [output-template.md](output-template.md) 的**字段规则与并存关系**输出结论。
|
|
20
|
+
- **项目隐性知识沉淀建议(必选,唯一在对话中展示的长文)**:在完成评审正文并**提交 comment 之后**,在对话里**只**输出这一段(可用 Markdown 小标题);**不要**在对话中重复贴评审模板全文。目的**不是**把需求写得更细,而是让**仓库侧**的文档、约定、可复用模式更清晰(必要时含代码组织/命名层面的改进方向),使日后**简短需求**也能被 AI 定位「改哪里、怎么改」,减少与「通用/行业默认」不一致时的重复追问(例如本项目的交互惯例、删除是否确认、与已有类似流程应对齐的隐性规则等)。须紧扣**本次需求内容**与**本次评审中暴露的、源于项目与常识差异的盲区**,给出可执行的沉淀项;**禁止**泛泛而谈。**不得**写入 **`.apm/workitems/<requirementId>/comment.md`**,**不得**作为 `apm set comment` 的正文内容。
|
|
21
|
+
- 语言面向产品可读:少堆砌实现细节,**不**用具体文件路径、函数名当「证据」;仓库仅用于理解**能力边界、术语与业务上下文**。
|
|
22
|
+
|
|
23
|
+
结构要点(写入 comment 的部分以 `output-template.md` 为准):`### 评审人`、`### 需求点 n`、`- **需求描述:**:` 及可选的 `业务合理性` / `问题` / `可行性` / `风险点`。**不包含**「隐性知识沉淀」类段落。
|
|
24
|
+
|
|
25
|
+
## 评审原则
|
|
26
|
+
|
|
27
|
+
1. **产品视角优先**:用户侧影响、业务闭环、风险与待确认点;避免晦涩技术术语堆砌。
|
|
28
|
+
2. **与仓库对齐(能力边界)**:阅读 `AGENTS.md`、子项目说明、`.apm/product-capability-inventory` 等与需求相关的部分,判断需求是否与既有能力/术语冲突、是否超出现有边界。**禁止**用「某文件某行」证明 PRD 对错;**禁止**根据代码**猜测** PRD 未写清的口径——口径不清应归入「问题」待产品澄清。
|
|
29
|
+
3. **业务合理性(按需)**:结合仓库可读的业务/产品上下文,判断需求是否想清楚、方案是否当下较优、是否与业务目标或流程冲突。**仅当**不合理、需再商榷或非当下较优时,才写 `- **业务合理性:**:`;合理则**不写**该字段。
|
|
30
|
+
4. **信息完备与落地(互斥规则)**:
|
|
31
|
+
- 仅当 PRD/需求信息**不足以**形成开发可执行描述(关键落点、口径、范围、汇总规则等无法确定)时,写 `- **问题:**`,此时**不写**可行性与风险点。
|
|
32
|
+
- 若已足以判断「可以实现」(非关键细节不阻塞落地),写 `- **可行性:**`(成本低/中/高 + 精简说明);影响面大时再追加 `- **风险点:**`。
|
|
33
|
+
5. **澄清优先但不泛问**:不阻塞落地则不提问;不问可推断的琐碎问题。
|
|
34
|
+
6. **隐性知识沉淀可执行**:对用户单独输出的每条沉淀建议应能回答「在仓库里补什么、能消除哪类隐性歧义」;若本次未发现值得沉淀的差异点,仍须给出一句结论(例如「未发现与通用假设显著偏离、需单独成文的隐性规则」)。
|
|
35
|
+
|
|
36
|
+
## 执行步骤
|
|
37
|
+
|
|
38
|
+
1. **获取需求正文**:`apm get requirement <requirementId>`,如果报错则停止后面的步骤
|
|
39
|
+
2. **读代码**:阅读仓库源码以及 `.apm/product-capability-inventory` 中与需求相关的条目;不展开无关模块代码。
|
|
40
|
+
3. **拆条**:多条诉求时拆成 `### 需求点 1..N`,每条独立走完「业务合理性(可选)→ 问题 或 可行性(+风险)」逻辑。
|
|
41
|
+
4. **成文(仅评审模板)**:按 `output-template.md` 拼出写入 comment 的 Markdown(顶格可加 `### 评审人` + 当前模型名);**不含**「隐性知识沉淀」段落;**随即**将**该字符串**写入 **`.apm/workitems/<requirementId>/comment.md`**(如果存在则直接覆盖更新)。
|
|
42
|
+
5. **自检**:每条是否都有「需求描述」;「问题」与「可行性/风险」是否互斥符合 `output-template.md`;是否误引代码路径作论据;comment 全文是否**未混入**隐性知识段落。
|
|
43
|
+
6. **提交评审记录**:`apm set comment <requirementId>`(正文与 comment.md 一致,仅为评审模板内容)。
|
|
44
|
+
7. **对用户的单独陈述**:在对话中**仅**输出「项目隐性知识沉淀建议」(见上文「输出」);开头可用一句告知「完整评审已写入 comment / 已提交,以下仅为隐性知识沉淀」;**不要**在对话中粘贴评审正文,也**不要**把隐性知识段写入 comment 或重复提交。
|
|
45
|
+
|
|
46
|
+
## 附加资源
|
|
47
|
+
|
|
48
|
+
- 输出结构与字段规则:[output-template.md](output-template.md)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: prd-sync
|
|
3
|
+
description: 根据评论与用户回复合并修订需求 PRD,仅当用户主动提及该技能时使用,依赖需求 ID。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 需求 PRD 修订与同步
|
|
7
|
+
|
|
8
|
+
本技能用于在**需求原文**之上,结合用户给出的**补充信息**,把零散说明整理成**结构清晰、可执行**的一版需求正文:先将定稿**覆盖**本地 `prd.md`,再通过 **`apm set requirement`** 将正文同步到服务端。补充信息**常见**为针对评审的逐条回应,亦可包含用户明确提供的其他修订说明;**仅**将用户明确要写入的口径并入正文,不替用户发挥。
|
|
9
|
+
|
|
10
|
+
## 触发条件
|
|
11
|
+
|
|
12
|
+
用户**明确声明**使用本技能(例如在对话中 @ 本 SKILL)
|
|
13
|
+
|
|
14
|
+
## 输入
|
|
15
|
+
|
|
16
|
+
- **`requirementId`(必须)**:需求 ID。
|
|
17
|
+
|
|
18
|
+
可选:**修订范围**(全文重写 / 只改某几节)、**术语表**、**必须保留的编号**。
|
|
19
|
+
|
|
20
|
+
## 合并原则
|
|
21
|
+
|
|
22
|
+
1. **正文依据 = 需求原文 + 补充信息**:只把用户在补充信息中**明确**要体现的内容(补充、修改、删除、拍板口径)合并进修订稿;用户没说到的,**保持原文或不写**,**不自作主张**补需求、不替用户「落实」评审建议。
|
|
23
|
+
2. **未回应的评审**:不列入「待确认」、不改成待办、不推断「仍有问题」;视为用户未要求在本次修订中处理(可能无此问题、暂不改、或评审误解)。
|
|
24
|
+
3. **评审的定位**:仅辅助理解补充信息在回应什么;补充信息与评审不一致时,**以补充信息为准**。
|
|
25
|
+
4. **消除重复**:同一议题在原文与补充信息中多处出现时,合并为**一处**表述。
|
|
26
|
+
5. **可追溯(轻量)**:可选「修订说明」概括相对原文的变化,且**只写补充信息实际带来的变化**,不罗列未采纳的评审。
|
|
27
|
+
6. **与仓库一致**:对照 `AGENTS.md`、`.apm/product-capability-inventory` 等,避免修订稿与用户已确认表述冲突;**禁止**用代码路径当需求论据。
|
|
28
|
+
7. **图片引用保持原样**:若原文含图片(如 `<img ...>`),在修订稿中直接保留对应 `img` 标签原文;不要改写成“图片见原始文档”等占位说明。
|
|
29
|
+
|
|
30
|
+
## 输出
|
|
31
|
+
|
|
32
|
+
- **默认**:输出 **Markdown** 形式的**更新后需求**(或用户指定章节),结构参考 [updated-requirement-template.md](updated-requirement-template.md);
|
|
33
|
+
- **用户只要补丁**:仍须合并为**完整一版**正文再存档(对话中可附「变更摘要」便于阅读)。
|
|
34
|
+
- **语言**:与需求原文一致(中文为主时全文中文)。
|
|
35
|
+
|
|
36
|
+
## 执行步骤
|
|
37
|
+
|
|
38
|
+
1. **拉取需求与评论(使用 CLI)**:执行 `apm get requirement <requirementId> --all`,获取需求文档及**全部评论**;该命令会将需求信息(含正文、版本与评论等)打印到终端,作为后续修订的**原文与评审依据**。
|
|
39
|
+
2. 以**原文**为底稿,对照**评审内容**,逐条落实**补充信息**中明确要求写进需求的修改。
|
|
40
|
+
3. 仅当某条补充信息明显对应某条评审时,再辅助定位修改位置;**跳过**用户完全未提的评审条目(仍须已提供评审全文/摘录以便对照,但不据此发挥)。
|
|
41
|
+
4. **「待确认」**:仅当**用户自己在补充信息里**留下未决口径、或明确说「待定」「再议」时写入;**不因**评审提了而用户未提供对应补充就填「待确认」。
|
|
42
|
+
5. 通读检查:无内部矛盾;正文无不来自补充信息的「新需求」。
|
|
43
|
+
6. **存档并同步到服务端(两步)**:
|
|
44
|
+
|
|
45
|
+
**6.1 覆盖本地需求文档**:将第 5 步定稿的**完整修订稿**写入 **`.apm/workitems/<requirementId>/prd.md`**(**覆盖**原文件,与 `apm get requirement` 拉取下来的路径一致)。
|
|
46
|
+
|
|
47
|
+
**6.2 同步到服务器**:在仓库根目录执行 **`apm set requirement <requirementId>`**
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
apm set requirement 1
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 自检清单
|
|
54
|
+
|
|
55
|
+
- [ ] 修订稿中的新增/变更均可追溯到补充信息(或用户要求保留的原文),无「替用户采纳评审」的发挥
|
|
56
|
+
- [ ] 未把未回应的评审当成待解决问题写进文档
|
|
57
|
+
- [ ] 未把评审人猜测当事实写进需求
|
|
58
|
+
- [ ] 修订稿单读可理解,不依赖聊天记录才能懂
|
|
59
|
+
- [ ] `.apm/workitems/<requirementId>/prd.md`与定稿修订稿一致,且已成功执行 `apm set requirement`
|
|
60
|
+
|
|
61
|
+
## 附加资源
|
|
62
|
+
|
|
63
|
+
- 推荐输出结构:[updated-requirement-template.md](updated-requirement-template.md)
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: requirement-doc-refine
|
|
3
|
-
description: 依据用户提供的补充信息(如对评审的回复、澄清与拍板口径)完善需求文档,合并定稿后经 CLI(apm comment process)存档。须同时提供需求原文、评审内容作对照与 requirementId、versionSeq;以补充信息为准,未回应的评审点不自动当成待办。仅在用户明确声明使用本技能时应用(例如 @ 本 SKILL 或写明 requirement-doc-refine);不因泛泛表述自动启用。
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# 需求文档完善(基于补充信息)
|
|
7
|
-
|
|
8
|
-
本技能用于在**需求原文**之上,结合用户给出的**补充信息**,把零散说明整理成**结构清晰、可执行**的一版需求正文,并通过 CLI 提交以写入需求内容版本。补充信息**常见**为针对评审的逐条回应,亦可包含用户明确提供的其他修订说明;**仅**将用户明确要写入的口径并入正文,不替用户发挥。
|
|
9
|
-
|
|
10
|
-
## 触发条件
|
|
11
|
-
|
|
12
|
-
- **必须**:用户**明确声明**使用本技能(例如在对话中 @ 本 `SKILL.md`、或写明使用 `requirement-doc-refine` /「用需求文档完善技能」等可核验的指代)。
|
|
13
|
-
- 未作上述声明时,**不**因「帮我完善需求」「合并补充信息进 PRD」「根据评审改需求」「更新需求文档」等表述自动套用本技能的工作方式与模板。
|
|
14
|
-
|
|
15
|
-
满足触发后,再按下节收集材料并执行;若缺「需求原文」「补充信息(用户回应)」「评审内容」或存档所需的 **`requirementId`**、**`versionSeq`**(修订所基于的版本序号),先索要再执行。
|
|
16
|
-
|
|
17
|
-
## 输入(按需收集)
|
|
18
|
-
|
|
19
|
-
声明使用本技能后,还须具备:
|
|
20
|
-
|
|
21
|
-
- **必需**:**需求原文**、**评审内容**、**补充信息(用户回应)**(含对哪些点做了补充;可不要求逐条对齐评审编号)、**`requirementId`**(需求 id,整数)、**`versionSeq`**(该需求下作为修订基准的版本序号,与界面 `v1`、`v2` 一致,非数据库 id),与定稿正文一起在首轮给出,供第 5 步 `apm comment process` 使用。
|
|
22
|
-
- **评审内容**:完整评审 Markdown(或与本次修订对应的摘录),用于把补充信息与评审条目对上号;**不得**据此单方面改需求。
|
|
23
|
-
|
|
24
|
-
| 输入 | 说明 |
|
|
25
|
-
|------|------|
|
|
26
|
-
| **需求原文** | 待完善的 PRD/需求全文或关键段落 |
|
|
27
|
-
| **评审内容** | 与本次修订对应的评审正文(或摘录);用于对照,**不得**单独驱动改写 |
|
|
28
|
-
| **补充信息(用户回应)** | 用户明确要写入需求或明确补充/修正的口径;**未提及的评审点一律不主动处理** |
|
|
29
|
-
| **`requirementId`** | 需求 id,对应 CLI `--requirement-id` |
|
|
30
|
-
| **`versionSeq`** | 修订所基于的版本序号(与界面 `v1`、`v2` 一致),对应 CLI `--version-seq` |
|
|
31
|
-
|
|
32
|
-
可选:**修订范围**(全文重写 / 只改某几节)、**术语表**、**必须保留的编号**。
|
|
33
|
-
|
|
34
|
-
## 合并原则
|
|
35
|
-
|
|
36
|
-
1. **正文依据 = 需求原文 + 补充信息**:只把用户在补充信息中**明确**要体现的内容(补充、修改、删除、拍板口径)合并进修订稿;用户没说到的,**保持原文或不写**,**不自作主张**补需求、不替用户「落实」评审建议。
|
|
37
|
-
2. **未回应的评审**:不列入「待确认」、不改成待办、不推断「仍有问题」;视为用户未要求在本次修订中处理(可能无此问题、暂不改、或评审误解)。
|
|
38
|
-
3. **评审的定位**:仅辅助理解补充信息在回应什么;补充信息与评审不一致时,**以补充信息为准**。
|
|
39
|
-
4. **消除重复**:同一议题在原文与补充信息中多处出现时,合并为**一处**表述。
|
|
40
|
-
5. **可追溯(轻量)**:可选「修订说明」概括相对原文的变化,且**只写补充信息实际带来的变化**,不罗列未采纳的评审。
|
|
41
|
-
6. **与仓库一致**:对照 `AGENTS.md`、`.apm/product-capability-inventory` 等,避免修订稿与用户已确认表述冲突;**禁止**用代码路径当需求论据。
|
|
42
|
-
7. **图片引用保持原样**:若原文含图片(如 `<img ...>`),在修订稿中直接保留对应 `img` 标签原文;不要改写成“图片见原始文档”等占位说明。
|
|
43
|
-
|
|
44
|
-
## 输出
|
|
45
|
-
|
|
46
|
-
- **默认**:输出 **Markdown** 形式的**更新后需求**(或用户指定章节),结构参考 [updated-requirement-template.md](updated-requirement-template.md);**定稿全文**作为第 5 步传给 `apm comment process` 的正文(`--content` 或 `--content-file`)。
|
|
47
|
-
- **用户只要补丁**:仍须合并为**完整一版**正文再存档(对话中可附「变更摘要」便于阅读)。
|
|
48
|
-
- **语言**:与需求原文一致(中文为主时全文中文)。
|
|
49
|
-
|
|
50
|
-
## 执行步骤
|
|
51
|
-
|
|
52
|
-
1. 以**原文**为底稿,对照**评审内容**,逐条落实**补充信息**中明确要求写进需求的修改。
|
|
53
|
-
2. 仅当某条补充信息明显对应某条评审时,再辅助定位修改位置;**跳过**用户完全未提的评审条目(仍须已提供评审全文/摘录以便对照,但不据此发挥)。
|
|
54
|
-
3. **「待确认」**:仅当**用户自己在补充信息里**留下未决口径、或明确说「待定」「再议」时写入;**不因**评审提了而用户未提供对应补充就填「待确认」。
|
|
55
|
-
4. 通读检查:无内部矛盾;正文无不来自补充信息的「新需求」。
|
|
56
|
-
5. **存档需求版本(使用 CLI)**:将第 4 步定稿的**完整修订稿**通过 CLI 命令提交,由服务端处理该版本下「待处理」评论并创建新版本(同步需求正文)。
|
|
57
|
-
|
|
58
|
-
- **命令**:`apm comment process`
|
|
59
|
-
- **必填参数**:
|
|
60
|
-
- `--requirement-id <id>`:需求 id(即本技能输入的 `requirementId`)
|
|
61
|
-
- `--version-seq <n>`:该需求下的版本序号(与界面 `1`、`2` 一致,非数据库 id)
|
|
62
|
-
- `--content <text>` 或 `--content-file <path>`:修订后的需求正文全文(二选一)
|
|
63
|
-
|
|
64
|
-
**示例(推荐文件方式,避免多行转义)**:
|
|
65
|
-
|
|
66
|
-
```bash
|
|
67
|
-
apm comment process \
|
|
68
|
-
--requirement-id 1 \
|
|
69
|
-
--version-seq 2 \
|
|
70
|
-
--content-file ./updated-requirement.md
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
**示例(直接传文本)**:
|
|
74
|
-
|
|
75
|
-
```bash
|
|
76
|
-
apm comment process \
|
|
77
|
-
--requirement-id 1 \
|
|
78
|
-
--version-seq 2 \
|
|
79
|
-
--content "这是要存档的一版需求正文内容……"
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
实际调用时把 `requirementId`、`versionSeq` 换成首轮给出的值,把 `content`(或 `content-file` 指向的文件内容)换成第 4 步成文结果。命令失败时说明错误信息;成功时可简要确认已创建新版本并返回处理结果。若用户**另行**要求写入仓库内某路径,再按需保存文件。
|
|
83
|
-
|
|
84
|
-
## 自检清单
|
|
85
|
-
|
|
86
|
-
- [ ] 修订稿中的新增/变更均可追溯到补充信息(或用户要求保留的原文),无「替用户采纳评审」的发挥
|
|
87
|
-
- [ ] 未把未回应的评审当成待解决问题写进文档
|
|
88
|
-
- [ ] 未把评审人猜测当事实写进需求
|
|
89
|
-
- [ ] 修订稿单读可理解,不依赖聊天记录才能懂
|
|
90
|
-
- [ ] `apm comment process` 传入的正文与定稿修订稿一致,`--requirement-id`、`--version-seq` 与用户给定一致
|
|
91
|
-
|
|
92
|
-
## 附加资源
|
|
93
|
-
|
|
94
|
-
- 推荐输出结构:[updated-requirement-template.md](updated-requirement-template.md)
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: requirement-review
|
|
3
|
-
description: 结合本仓库上下文对需求做结构化评审,只有当用户主动提及该技能时才可被使用,该技能调用依赖需求ID。
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# 需求评审
|
|
7
|
-
|
|
8
|
-
## 触发条件
|
|
9
|
-
|
|
10
|
-
用户**明确声明**使用本技能(例如在对话中 @ 本 SKILL)
|
|
11
|
-
|
|
12
|
-
## 输入
|
|
13
|
-
|
|
14
|
-
- **`requirementId`(必须)**:需求 ID。
|
|
15
|
-
|
|
16
|
-
## 输出
|
|
17
|
-
|
|
18
|
-
- **评审正文**:须先按模板在**逻辑上**成文(完整 Markdown 字符串),不得仅给零散要点,。可在回复中展示。
|
|
19
|
-
- 将需求拆成若干条「需求点」(若本就一条则一条),对每条按 [output-template.md](output-template.md) 的**字段规则与并存关系**输出结论。
|
|
20
|
-
- 语言面向产品可读:少堆砌实现细节,**不**用具体文件路径、函数名当「证据」;仓库仅用于理解**能力边界、术语与业务上下文**。
|
|
21
|
-
|
|
22
|
-
结构要点(细节以 `output-template.md` 为准):`### 需求点 n`、`- **需求描述:**:` 及可选的 `业务合理性` / `问题` / `可行性` / `风险点`;。
|
|
23
|
-
|
|
24
|
-
## 评审原则
|
|
25
|
-
|
|
26
|
-
1. **产品视角优先**:用户侧影响、业务闭环、风险与待确认点;避免晦涩技术术语堆砌。
|
|
27
|
-
2. **与仓库对齐(能力边界)**:阅读 `AGENTS.md`、子项目说明、`.apm/product-capability-inventory` 等与需求相关的部分,判断需求是否与既有能力/术语冲突、是否超出现有边界。**禁止**用「某文件某行」证明 PRD 对错;**禁止**根据代码**猜测** PRD 未写清的口径——口径不清应归入「问题」待产品澄清。
|
|
28
|
-
3. **业务合理性(按需)**:结合仓库可读的业务/产品上下文,判断需求是否想清楚、方案是否当下较优、是否与业务目标或流程冲突。**仅当**不合理、需再商榷或非当下较优时,才写 `- **业务合理性:**:`;合理则**不写**该字段。
|
|
29
|
-
4. **信息完备与落地(互斥规则)**:
|
|
30
|
-
- 仅当 PRD/需求信息**不足以**形成开发可执行描述(关键落点、口径、范围、汇总规则等无法确定)时,写 `- **问题:**`,此时**不写**可行性与风险点。
|
|
31
|
-
- 若已足以判断「可以实现」(非关键细节不阻塞落地),写 `- **可行性:**`(成本低/中/高 + 精简说明);影响面大时再追加 `- **风险点:**`。
|
|
32
|
-
5. **澄清优先但不泛问**:不阻塞落地则不提问;不问可推断的琐碎问题。
|
|
33
|
-
|
|
34
|
-
## 执行步骤
|
|
35
|
-
|
|
36
|
-
1. **获取需求正文**:`apm get requirement <requirementId>`,如果报错则停止后面的步骤
|
|
37
|
-
2. **读代码**:阅读仓库源码以及 `.apm/product-capability-inventory` 中与需求相关的条目;不展开无关模块代码。
|
|
38
|
-
3. **拆条**:多条诉求时拆成 `### 需求点 1..N`,每条独立走完「业务合理性(可选)→ 问题 或 可行性(+风险)」逻辑。
|
|
39
|
-
4. **成文**:按 `output-template.md` 拼出完整 Markdown 字符串(顶格可加 `### 评审人` + 当前模型名);**随即**将全文写入 **`.apm/workitems/<requirementId>/comment.md`**(如果存在则直接覆盖更新)。
|
|
40
|
-
5. **自检**:每条是否都有「需求描述」;「问题」与「可行性/风险」是否互斥符合 `output-template.md`;是否误引代码路径作论据。
|
|
41
|
-
6. **提交评审记录**:`apm set comment <requirementId>`。
|
|
42
|
-
|
|
43
|
-
## 附加资源
|
|
44
|
-
|
|
45
|
-
- 输出结构与字段规则:[output-template.md](output-template.md)
|
/package/templates/skills/{requirement-doc-refine → prd-sync}/updated-requirement-template.md
RENAMED
|
File without changes
|