@openprd/cli 0.1.0 → 0.1.8
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/.openprd/README.md +43 -69
- package/.openprd/README_EN.md +84 -0
- package/.openprd/benchmarks/index.md +7 -0
- package/.openprd/benchmarks/sources.yaml +25 -3
- package/.openprd/discovery/config.json +16 -2
- package/.openprd/engagements/active/flows.md +19 -14
- package/.openprd/engagements/active/handoff.md +11 -4
- package/.openprd/engagements/active/prd.md +99 -71
- package/.openprd/engagements/active/review.html +4 -4
- package/.openprd/engagements/active/roles.md +9 -8
- package/.openprd/engagements/work-units/wu-20260524015648-6d33ded7.json +4 -4
- package/.openprd/engagements/work-units/wu-20260602113956-a99b5b88.json +18 -0
- package/.openprd/engagements/work-units/wu-20260602122244-78656aaf.json +18 -0
- package/.openprd/engagements/work-units/wu-20260602122442-e96489e2.json +18 -0
- package/.openprd/engagements/work-units/wu-20260602132835-695429e8.json +18 -0
- package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/candidate.json +78 -0
- package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/diagnostic-report.json +129 -0
- package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/root-cause-candidates.json +41 -0
- package/.openprd/knowledge/candidates/candidate-turn-1780116203372-5f266a79e968c758/timeline.json +14 -0
- package/.openprd/knowledge/drafts/openprd-experience-diagnostic-candidate-turn-1780116203372-5f266a79e968c758/SKILL.md +49 -0
- package/.openprd/knowledge/index.json +44 -4
- package/.openprd/reviews/v0001.html +195 -129
- package/.openprd/reviews/v0002.html +1150 -0
- package/.openprd/reviews/v0003.html +1150 -0
- package/.openprd/reviews/v0004.html +1150 -0
- package/.openprd/reviews/v0005.html +1150 -0
- package/.openprd/standards/config.json +12 -9
- package/.openprd/state/changes.json +17 -2
- package/.openprd/state/current.json +399 -63
- package/.openprd/state/release-ledger.json +344 -0
- package/.openprd/state/version-index.json +52 -0
- package/.openprd/state/versions/v0002.json +264 -0
- package/.openprd/state/versions/v0002.md +183 -0
- package/.openprd/state/versions/v0003.json +269 -0
- package/.openprd/state/versions/v0003.md +188 -0
- package/.openprd/state/versions/v0004.json +274 -0
- package/.openprd/state/versions/v0004.md +193 -0
- package/.openprd/state/versions/v0005.json +299 -0
- package/.openprd/state/versions/v0005.md +189 -0
- package/.openprd/templates/agent/intake.md +5 -4
- package/.openprd/templates/b2b/intake.md +5 -4
- package/.openprd/templates/base/intake.md +10 -4
- package/.openprd/templates/company/README.md +9 -7
- package/.openprd/templates/company/README_EN.md +12 -0
- package/.openprd/templates/consumer/intake.md +5 -4
- package/.openprd/templates/industry/README.md +12 -10
- package/.openprd/templates/industry/README_EN.md +18 -0
- package/.openprd/templates/project/README.md +11 -9
- package/.openprd/templates/project/README_EN.md +16 -0
- package/.openprd/templates/session/README.md +11 -9
- package/.openprd/templates/session/README_EN.md +16 -0
- package/AGENTS.md +12 -8
- package/README.md +402 -441
- package/README_CN.md +4 -578
- package/README_EN.md +850 -0
- package/docs/assets/openprd-requirement-routing-en.png +0 -0
- package/docs/assets/openprd-requirement-routing-en.svg +102 -0
- package/docs/assets/openprd-requirement-routing-zh-refined.png +0 -0
- package/docs/assets/openprd-requirement-routing-zh.png +0 -0
- package/docs/assets/openprd-requirement-routing-zh.svg +102 -0
- package/package.json +6 -2
- package/scripts/dev-check-wrapup-copy.mjs +110 -0
- package/scripts/openprd-github-release-notes.mjs +99 -0
- package/scripts/quality-perf-check.mjs +203 -0
- package/skills/openprd-benchmark-router/SKILL.md +1 -0
- package/skills/openprd-benchmark-router/references/benchmark-sources.md +1 -0
- package/skills/openprd-benchmark-router/references/source-policy.md +2 -0
- package/skills/openprd-discovery-loop/SKILL.md +2 -2
- package/skills/openprd-harness/SKILL.md +46 -24
- package/skills/openprd-harness/references/workflow-gates.md +15 -0
- package/skills/openprd-quality/SKILL.md +10 -4
- package/skills/openprd-requirement-intake/SKILL.md +39 -23
- package/skills/openprd-requirement-intake/references/prd-template-lenses.md +6 -6
- package/skills/openprd-requirement-intake/references/routing-rubric.md +22 -8
- package/skills/openprd-router/SKILL.md +2 -2
- package/skills/openprd-shared/SKILL.md +51 -23
- package/skills/openprd-standards/SKILL.md +2 -1
- package/src/agent-integration.js +265 -65
- package/src/benchmark/constants.js +107 -0
- package/src/benchmark/operations.js +235 -0
- package/src/benchmark/registry.js +64 -0
- package/src/benchmark/render.js +115 -0
- package/src/benchmark/source.js +617 -0
- package/src/benchmark/storage.js +121 -0
- package/src/benchmark/verify.js +235 -0
- package/src/benchmark.js +50 -851
- package/src/change-summary.js +339 -0
- package/src/cli/args.js +67 -6
- package/src/cli/basic-print.js +365 -0
- package/src/cli/benchmark-print.js +91 -0
- package/src/cli/change-print.js +221 -0
- package/src/cli/doctor-print.js +268 -0
- package/src/cli/growth-print.js +176 -0
- package/src/cli/print.js +73 -1384
- package/src/cli/quality-print.js +284 -0
- package/src/cli/run-print.js +297 -0
- package/src/cli/shared-print.js +127 -0
- package/src/cli/workflow-print.js +195 -0
- package/src/codex-hook-runner-template.mjs +639 -117
- package/src/codex-runtime.js +324 -0
- package/src/dev-standards.js +178 -5
- package/src/diagram-core.js +5 -5
- package/src/discovery.js +2 -1
- package/src/execution-strategy.js +369 -0
- package/src/fleet.js +4 -0
- package/src/github-release.js +156 -0
- package/src/growth.js +311 -13
- package/src/html-artifact-utils.js +25 -0
- package/src/html-artifacts.js +157 -1596
- package/src/knowledge.js +1176 -75
- package/src/language-policy.js +2 -112
- package/src/learning-html-artifact.js +1031 -0
- package/src/learning-review.js +3 -2
- package/src/loop.js +280 -9
- package/src/openprd.js +341 -38
- package/src/openspec/change-validate.js +0 -9
- package/src/openspec/execute.js +79 -3
- package/src/openspec/generate.js +33 -20
- package/src/openspec/tasks.js +33 -2
- package/src/prd-core.js +10 -9
- package/src/product-type-copy.js +69 -0
- package/src/quality-html-artifact.js +108 -9
- package/src/quality-learning.js +30 -0
- package/src/quality-visual-review.js +237 -0
- package/src/quality.js +329 -43
- package/src/registry-hygiene.js +54 -0
- package/src/release-ledger.js +413 -0
- package/src/review-presentation.js +12 -6
- package/src/run-harness.js +722 -48
- package/src/self-update.js +1 -1
- package/src/session-binding.js +40 -3
- package/src/session-registry.js +159 -0
- package/src/standards.js +5 -3
- package/src/test-strategy.js +386 -0
- package/src/visual-compare.js +915 -34
- package/src/work-unit-migration.js +5 -1
- package/src/workspace-core.js +343 -19
- package/src/workspace-workflow.js +538 -134
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* 核心功能
|
|
3
|
+
* 渲染 standards、dev-check、quality、knowledge 和 visual compare 等质量相关命令输出。
|
|
4
|
+
*
|
|
5
|
+
* 输入
|
|
6
|
+
* 接收标准校验、研发期行数检查、质量报告、知识候选和视觉对比等结构化结果对象。
|
|
7
|
+
*
|
|
8
|
+
* 输出
|
|
9
|
+
* 向终端输出质量门禁、证据状态、知识候选和视觉对比摘要,或在 `--json` 模式下直出 JSON。
|
|
10
|
+
*
|
|
11
|
+
* 定位
|
|
12
|
+
* 位于 CLI 表现层的质量输出模块,负责 standards/quality 这一类高频质量反馈。
|
|
13
|
+
*
|
|
14
|
+
* 依赖
|
|
15
|
+
* 依赖 shared-print 中的知识回顾 helper,不承担实际检测逻辑。
|
|
16
|
+
*
|
|
17
|
+
* 维护规则
|
|
18
|
+
* 质量类输出要优先保留 production-ready、门禁范围和证据状态,避免把关键风险埋进长文本里。
|
|
19
|
+
*/
|
|
20
|
+
import { printKnowledgeReview } from './shared-print.js';
|
|
21
|
+
|
|
22
|
+
function describeFeatureCoverageLedger(report) {
|
|
23
|
+
const activeTasks = report?.evalHarness?.featureCoverage?.activeTasks ?? null;
|
|
24
|
+
if (!activeTasks || Number(activeTasks.pending ?? 0) <= 0) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const total = Number(activeTasks.total ?? 0);
|
|
28
|
+
const done = Number(activeTasks.done ?? 0);
|
|
29
|
+
const pending = Number(activeTasks.pending ?? 0);
|
|
30
|
+
const blocked = Number(activeTasks.blocked ?? 0);
|
|
31
|
+
const progress = total > 0 ? `${done}/${total}` : `${done}`;
|
|
32
|
+
const changeLabel = activeTasks.activeChange ? `当前变更 ${activeTasks.activeChange}` : '当前任务账本';
|
|
33
|
+
return `${changeLabel} 仍有 ${pending} 个未完成任务(已完成 ${progress}${blocked > 0 ? `,其中 ${blocked} 个因依赖阻塞` : ''})。这表示账本未收口,不等于当前实现失败。`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function printStandardsResult(result, json) {
|
|
37
|
+
if (json) {
|
|
38
|
+
console.log(JSON.stringify(result, null, 2));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (result.action === 'init') {
|
|
43
|
+
console.log(`已初始化 OpenPrd standards: ${result.docsRoot}`);
|
|
44
|
+
for (const item of result.changed) {
|
|
45
|
+
console.log(`- ${item.status}: ${item.path}`);
|
|
46
|
+
}
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (result.action === 'classify-external-reference') {
|
|
50
|
+
console.log(`已归类外部参考源码: ${result.path}`);
|
|
51
|
+
console.log(`配置: ${result.configPath}`);
|
|
52
|
+
console.log(`状态: ${result.alreadyPresent ? '已存在' : '已写入'}`);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
console.log(`OpenPrd standards: ${result.ok ? '通过' : '失败'}`);
|
|
57
|
+
console.log(`Docs root: ${result.docsRoot}`);
|
|
58
|
+
for (const check of result.checks) {
|
|
59
|
+
console.log(`- ${check}`);
|
|
60
|
+
}
|
|
61
|
+
if (result.errors.length > 0) {
|
|
62
|
+
console.log('错误:');
|
|
63
|
+
for (const error of result.errors) {
|
|
64
|
+
console.log(`- ${error}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (result.warnings.length > 0) {
|
|
68
|
+
console.log('警告:');
|
|
69
|
+
for (const warning of result.warnings) {
|
|
70
|
+
console.log(`- ${warning}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const candidates = result.manualReport?.externalReferenceCandidates ?? [];
|
|
74
|
+
if (candidates.length > 0) {
|
|
75
|
+
console.log('外部参考源码候选:');
|
|
76
|
+
console.log('请先询问用户这些目录是否只作为外部参考;用户确认后再运行归类命令。');
|
|
77
|
+
for (const candidate of candidates) {
|
|
78
|
+
console.log(`- ${candidate.path}: ${candidate.missingFiles} 个文件、${candidate.missingFolders} 个文件夹缺说明书;原因: ${candidate.reason};建议确认后运行 ${candidate.suggestedCommand}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function printDevelopmentStandardsResult(result, json) {
|
|
84
|
+
if (json) {
|
|
85
|
+
console.log(JSON.stringify(result, null, 2));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
console.log(`OpenPrd 后续建议: ${result.ok ? '完成' : '失败'}`);
|
|
90
|
+
const attentionFiles = result.files.filter((file) => ['attention', 'warning', 'error'].includes(file.status));
|
|
91
|
+
if (attentionFiles.length > 0) {
|
|
92
|
+
console.log(`关注程度: 🟡 低风险|建议留意,🟠 中风险|建议优先关注,🔴 高风险|需要先处理。`);
|
|
93
|
+
} else {
|
|
94
|
+
console.log(`本轮已回顾 ${result.files.length} 个改动文件,暂未发现需要额外说明的影响对象。`);
|
|
95
|
+
}
|
|
96
|
+
for (const file of attentionFiles) {
|
|
97
|
+
const lineText = file.lineCount === null || file.lineCount === undefined ? '未知行数' : `${file.lineCount} 行`;
|
|
98
|
+
console.log(`- ${file.statusLabel ?? file.status}: ${file.path} (${lineText})`);
|
|
99
|
+
console.log(` ${file.nextAction}`);
|
|
100
|
+
}
|
|
101
|
+
if (result.wrapUp?.required && result.wrapUp.markdownTable) {
|
|
102
|
+
console.log(result.wrapUp.markdownBlock ?? result.wrapUp.markdownTable);
|
|
103
|
+
}
|
|
104
|
+
printKnowledgeReview(result.knowledgeReview);
|
|
105
|
+
for (const error of result.errors ?? []) {
|
|
106
|
+
console.log(`- ${error}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function printQualityResult(result, json) {
|
|
111
|
+
if (json) {
|
|
112
|
+
console.log(JSON.stringify(result, null, 2));
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (result.action === 'quality-init') {
|
|
117
|
+
console.log(`OpenPrd quality: 已初始化 (${result.changed})`);
|
|
118
|
+
console.log(`配置: ${result.files.config}`);
|
|
119
|
+
console.log(`报告目录: ${result.files.reportsDir}`);
|
|
120
|
+
console.log(`知识库索引: ${result.files.knowledgeIndex}`);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (result.action === 'quality-learn') {
|
|
125
|
+
console.log(`OpenPrd quality learn: ${result.ok ? '已沉淀' : '失败'}`);
|
|
126
|
+
if (result.ok) {
|
|
127
|
+
console.log(`来源类型: ${result.sourceKind}`);
|
|
128
|
+
console.log(`来源: ${result.sourcePath}`);
|
|
129
|
+
if (Array.isArray(result.sourcePaths) && result.sourcePaths.length > 1) {
|
|
130
|
+
console.log(`证据数: ${result.sourcePaths.length}`);
|
|
131
|
+
}
|
|
132
|
+
console.log(`事故: ${result.files.incident}`);
|
|
133
|
+
console.log(`模式: ${result.files.pattern}`);
|
|
134
|
+
console.log(`经验 Skill: ${result.files.skill}`);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
for (const error of result.errors ?? []) {
|
|
138
|
+
console.log(`- ${error}`);
|
|
139
|
+
}
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (result.action === 'quality-knowledge-review') {
|
|
144
|
+
console.log('OpenPrd quality review: 已完成');
|
|
145
|
+
printKnowledgeReview(result);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const headline = result.report
|
|
150
|
+
? (result.report.readiness.ok && !result.report.readiness.productionReady
|
|
151
|
+
? '需关注'
|
|
152
|
+
: (result.ok ? '完成' : '失败'))
|
|
153
|
+
: (result.ok ? '完成' : '失败');
|
|
154
|
+
console.log(`OpenPrd quality: ${headline}`);
|
|
155
|
+
if (result.report) {
|
|
156
|
+
console.log(`质量状态: ${result.report.summary.status}`);
|
|
157
|
+
console.log(`生产就绪: ${result.report.readiness.productionReady ? '是' : '否'}`);
|
|
158
|
+
console.log(`执行模式: ${result.report.readiness.enforcement}`);
|
|
159
|
+
if (result.report.qualityPolicy) {
|
|
160
|
+
console.log(`场景标签: ${result.report.qualityPolicy.scenarioTags.join(', ')}`);
|
|
161
|
+
console.log(`必需门禁: ${result.report.qualityPolicy.requiredGates.join(', ') || '无'}`);
|
|
162
|
+
}
|
|
163
|
+
if (result.report.readiness.attentionGates.length > 0) {
|
|
164
|
+
console.log(`需关注门禁: ${result.report.readiness.attentionGates.join(', ')}`);
|
|
165
|
+
}
|
|
166
|
+
const featureCoverageLedger = describeFeatureCoverageLedger(result.report);
|
|
167
|
+
if (featureCoverageLedger) {
|
|
168
|
+
console.log(`账本提示: ${featureCoverageLedger}`);
|
|
169
|
+
}
|
|
170
|
+
console.log('门禁:');
|
|
171
|
+
for (const gate of result.report.gates) {
|
|
172
|
+
const scope = gate.required ? '必需' : '可选';
|
|
173
|
+
const evidence = gate.evidence?.present ? `证据 ${gate.evidence.sources.length}` : '缺证据';
|
|
174
|
+
console.log(`- ${gate.status}: ${gate.label} (${scope}, ${evidence})`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (result.reportPath) {
|
|
178
|
+
console.log(`JSON: ${result.reportPath}`);
|
|
179
|
+
}
|
|
180
|
+
if (result.htmlPath) {
|
|
181
|
+
console.log(`HTML: ${result.htmlPath}`);
|
|
182
|
+
}
|
|
183
|
+
printKnowledgeReview(result.knowledgeReview);
|
|
184
|
+
for (const error of result.errors ?? []) {
|
|
185
|
+
console.log(`- ${error}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function printKnowledgeResult(result, json) {
|
|
190
|
+
if (json) {
|
|
191
|
+
console.log(JSON.stringify(result, null, 2));
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (!result.ok) {
|
|
196
|
+
console.log(`OpenPrd knowledge: 失败 (${result.action ?? 'unknown'})`);
|
|
197
|
+
for (const error of result.errors ?? []) {
|
|
198
|
+
console.log(`- ${error}`);
|
|
199
|
+
}
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (result.action === 'knowledge-candidates') {
|
|
204
|
+
const counts = result.counts ?? {};
|
|
205
|
+
console.log(`OpenPrd knowledge candidates: ${result.candidates.length} 个 (${result.status})`);
|
|
206
|
+
console.log(`统计: pending ${counts.pending ?? 0}, promoted ${counts.promoted ?? 0}, rejected ${counts.rejected ?? 0}, archived ${counts.archived ?? 0}, reviewed ${counts.reviewed ?? 0}, total ${counts.total ?? 0}`);
|
|
207
|
+
for (const candidate of result.candidates) {
|
|
208
|
+
console.log(`- ${candidate.candidateId}: ${candidate.title ?? candidate.candidateId}`);
|
|
209
|
+
console.log(` 状态: ${candidate.status}`);
|
|
210
|
+
console.log(` 候选: ${candidate.path ?? candidate.files?.candidate}`);
|
|
211
|
+
if (candidate.draftSkillPath ?? candidate.files?.draftSkill) {
|
|
212
|
+
console.log(` 草案 Skill: ${candidate.draftSkillPath ?? candidate.files?.draftSkill}`);
|
|
213
|
+
}
|
|
214
|
+
if (candidate.pending) {
|
|
215
|
+
console.log(` 拒绝: openprd knowledge reject --id ${candidate.candidateId} --reason <原因>`);
|
|
216
|
+
console.log(` 归档: openprd knowledge archive --id ${candidate.candidateId} --reason <原因>`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (result.candidates.length === 0) {
|
|
220
|
+
console.log('没有匹配的 knowledge candidate。');
|
|
221
|
+
}
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
console.log(`OpenPrd knowledge ${result.action?.replace(/^knowledge-/, '') ?? 'update'}: ${result.candidateId}`);
|
|
226
|
+
if (result.candidate) {
|
|
227
|
+
console.log(`状态: ${result.candidate.status}`);
|
|
228
|
+
if (result.candidate.reviewDecision) {
|
|
229
|
+
console.log(`决定: ${result.candidate.reviewDecision}`);
|
|
230
|
+
}
|
|
231
|
+
if (result.candidate.reviewReason) {
|
|
232
|
+
console.log(`原因: ${result.candidate.reviewReason}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (result.files?.candidate) {
|
|
236
|
+
console.log(`候选: ${result.files.candidate}`);
|
|
237
|
+
}
|
|
238
|
+
if (result.files?.knowledgeIndex) {
|
|
239
|
+
console.log(`索引: ${result.files.knowledgeIndex}`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function printVisualCompareResult(result, json) {
|
|
244
|
+
if (json) {
|
|
245
|
+
console.log(JSON.stringify(result, null, 2));
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const modeLabel = result.mode === 'before-after'
|
|
250
|
+
? '修改前后自检'
|
|
251
|
+
: result.mode === 'focus-board'
|
|
252
|
+
? '局部焦点证据板'
|
|
253
|
+
: result.mode === 'parallel-board'
|
|
254
|
+
? '并行实验证据板'
|
|
255
|
+
: '效果图对比';
|
|
256
|
+
console.log('OpenPrd visual compare: 已生成');
|
|
257
|
+
console.log(`输出图片: ${result.outputPath}`);
|
|
258
|
+
console.log(`模式: ${modeLabel}`);
|
|
259
|
+
console.log(`格式: ${result.format}${result.quality ? `, quality=${result.quality}` : ''}`);
|
|
260
|
+
console.log(`画布: ${result.canvas.width}x${result.canvas.height}`);
|
|
261
|
+
if (result.labels?.reference && result.reference?.rendered) {
|
|
262
|
+
console.log(`左侧: ${result.labels.reference} (${result.reference.rendered.width}x${result.reference.rendered.height})`);
|
|
263
|
+
}
|
|
264
|
+
if (result.labels?.actual && result.actual?.rendered) {
|
|
265
|
+
console.log(`右侧: ${result.labels.actual} (${result.actual.rendered.width}x${result.actual.rendered.height})`);
|
|
266
|
+
}
|
|
267
|
+
if (Array.isArray(result.focusRegions) && result.focusRegions.length > 0) {
|
|
268
|
+
console.log(`焦点区域: ${result.focusRegions.length}`);
|
|
269
|
+
}
|
|
270
|
+
if (Array.isArray(result.items) && result.items.length > 0) {
|
|
271
|
+
console.log(`实验卡片: ${result.items.length}`);
|
|
272
|
+
}
|
|
273
|
+
for (const action of result.nextActions ?? []) {
|
|
274
|
+
console.log(`- 下一步: ${action}`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export {
|
|
279
|
+
printStandardsResult,
|
|
280
|
+
printDevelopmentStandardsResult,
|
|
281
|
+
printQualityResult,
|
|
282
|
+
printKnowledgeResult,
|
|
283
|
+
printVisualCompareResult,
|
|
284
|
+
};
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* 核心功能
|
|
3
|
+
* 渲染 run / loop 相关命令的人类可读输出或 JSON 输出。
|
|
4
|
+
*
|
|
5
|
+
* 输入
|
|
6
|
+
* 接收 run context、run verify、loop prompt/run/finish 和执行确认清单等结构化结果对象。
|
|
7
|
+
*
|
|
8
|
+
* 输出
|
|
9
|
+
* 向终端输出当前执行流、任务状态、Loop 运行信息和知识回顾摘要。
|
|
10
|
+
*
|
|
11
|
+
* 定位
|
|
12
|
+
* 位于 CLI 表现层的执行路径输出模块,专门承接 run/loop 相关的密集终端信息。
|
|
13
|
+
*
|
|
14
|
+
* 依赖
|
|
15
|
+
* 依赖 shared-print 与 doctor-print 提供的复用 helper,不承担实际执行。
|
|
16
|
+
*
|
|
17
|
+
* 维护规则
|
|
18
|
+
* run/loop 输出字段变更时要保持任务态、验证态和确认清单的展示顺序稳定,避免遗漏执行门槛。
|
|
19
|
+
*/
|
|
20
|
+
import { labelExecutionMode } from '../execution-strategy.js';
|
|
21
|
+
import { printCodexRuntimeResult } from './doctor-print.js';
|
|
22
|
+
import { printKnowledgeReview, printKnowledgeSkillMatches } from './shared-print.js';
|
|
23
|
+
|
|
24
|
+
function printExecutionConfirmationChecklist(checklist) {
|
|
25
|
+
if (!checklist?.required) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
console.log(`${checklist.title ?? '开始动手前先确认这些'}:`);
|
|
29
|
+
if (checklist.objective) {
|
|
30
|
+
console.log(`- 这次要做什么: ${checklist.objective}`);
|
|
31
|
+
}
|
|
32
|
+
if (checklist.scope?.length > 0) {
|
|
33
|
+
console.log(`- 这次范围: ${checklist.scope.join(';')}`);
|
|
34
|
+
}
|
|
35
|
+
if (checklist.implementationItems?.length > 0) {
|
|
36
|
+
console.log(`- 我会这样推进: ${checklist.implementationItems.join(';')}`);
|
|
37
|
+
}
|
|
38
|
+
if (checklist.outOfScope?.length > 0) {
|
|
39
|
+
console.log(`- 这次先不做: ${checklist.outOfScope.join(';')}`);
|
|
40
|
+
}
|
|
41
|
+
if (checklist.verification?.length > 0) {
|
|
42
|
+
console.log(`- 完成后会检查: ${checklist.verification.join(';')}`);
|
|
43
|
+
}
|
|
44
|
+
if (checklist.risks?.length > 0) {
|
|
45
|
+
console.log(`- 需要提前知道: ${checklist.risks.join(';')}`);
|
|
46
|
+
}
|
|
47
|
+
if (checklist.confirmationPrompt) {
|
|
48
|
+
console.log(`- 如果要我现在继续: ${checklist.confirmationPrompt}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function printRunResult(result, json) {
|
|
53
|
+
if (json) {
|
|
54
|
+
console.log(JSON.stringify(result, null, 2));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (result.action === 'run-record-hook') {
|
|
59
|
+
console.log(`OpenPrd run hook 已记录: ${result.event.eventName} -> ${result.event.outcome}`);
|
|
60
|
+
console.log(`迭代记录: ${result.files.iterations}`);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (result.action === 'run-verify') {
|
|
65
|
+
const taskReady = result.readiness?.taskReady !== false;
|
|
66
|
+
const workspaceReady = result.readiness?.workspaceReady !== false;
|
|
67
|
+
const workspaceAttention = result.workspaceAttention ?? null;
|
|
68
|
+
const status = taskReady
|
|
69
|
+
? (workspaceReady ? '通过' : '当前任务通过,工作区待关注')
|
|
70
|
+
: '当前任务失败';
|
|
71
|
+
console.log(`OpenPrd run verify: ${status}`);
|
|
72
|
+
if (result.readiness) {
|
|
73
|
+
console.log(`任务就绪: ${taskReady ? '是' : '否'}`);
|
|
74
|
+
console.log(`工作区就绪: ${workspaceReady ? '是' : '否'}`);
|
|
75
|
+
if (result.readiness.qualityProductionReady !== null) {
|
|
76
|
+
console.log(`质量门禁: ${result.readiness.qualityProductionReady ? 'production-ready' : '待补证据'}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
for (const check of result.checks) {
|
|
80
|
+
const scope = check.scope === 'workspace' ? '工作区' : '任务';
|
|
81
|
+
const detail = check.name === 'quality' && check.productionReady === false
|
|
82
|
+
? ' (production-ready=false)'
|
|
83
|
+
: '';
|
|
84
|
+
console.log(`- ${check.ok ? '通过' : '失败'}: ${check.name} [${scope}]${detail}`);
|
|
85
|
+
}
|
|
86
|
+
if (workspaceAttention?.kind === 'feature-coverage-ledger') {
|
|
87
|
+
const changeLabel = workspaceAttention.activeChange
|
|
88
|
+
? `激活变更 ${workspaceAttention.activeChange}`
|
|
89
|
+
: '当前激活任务';
|
|
90
|
+
const progress = workspaceAttention.total > 0
|
|
91
|
+
? `${workspaceAttention.done}/${workspaceAttention.total}`
|
|
92
|
+
: `${workspaceAttention.done}`;
|
|
93
|
+
console.log('工作区待关注原因: feature-coverage 账本未收口');
|
|
94
|
+
console.log(`- ${changeLabel} 已完成 ${progress},待处理 ${workspaceAttention.pending}${workspaceAttention.blocked > 0 ? `,阻塞 ${workspaceAttention.blocked}` : ''}`);
|
|
95
|
+
console.log('- 这通常表示任务账本或覆盖证据还没补齐,不等于本次功能失败。');
|
|
96
|
+
} else if (workspaceAttention?.summary) {
|
|
97
|
+
console.log(`工作区待关注原因: ${workspaceAttention.summary}`);
|
|
98
|
+
}
|
|
99
|
+
printKnowledgeReview(result.knowledgeReview);
|
|
100
|
+
const extraWarnings = workspaceAttention?.detail
|
|
101
|
+
? result.warnings.filter((warning) => warning !== `quality: ${workspaceAttention.detail}`)
|
|
102
|
+
: result.warnings;
|
|
103
|
+
if (extraWarnings.length > 0) {
|
|
104
|
+
console.log('工作区待关注:');
|
|
105
|
+
for (const warning of extraWarnings) {
|
|
106
|
+
console.log(`- ${warning}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (result.errors.length > 0) {
|
|
110
|
+
console.log('错误:');
|
|
111
|
+
for (const error of result.errors) {
|
|
112
|
+
console.log(`- ${error}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
console.log('当前进展参考');
|
|
119
|
+
console.log(`当前项目: ${result.projectRoot}`);
|
|
120
|
+
console.log(`基础检查: ${result.validation.valid ? '通过' : '失败'}`);
|
|
121
|
+
if (result.lane?.summary) {
|
|
122
|
+
console.log(`当前处理路径: ${result.lane.summary}`);
|
|
123
|
+
}
|
|
124
|
+
if (result.activeChange) {
|
|
125
|
+
const label = result.recommendation?.type === 'requirement-intake' ? '历史聚焦事项' : '当前聚焦事项';
|
|
126
|
+
console.log(`${label}: ${result.activeChange}`);
|
|
127
|
+
}
|
|
128
|
+
if (result.focus?.changeId && result.focus.changeId !== result.activeChange) {
|
|
129
|
+
console.log(`当前补充焦点: ${result.focus.changeId}`);
|
|
130
|
+
}
|
|
131
|
+
if (result.activeRequirementGate) {
|
|
132
|
+
const gateStatus = result.activeRequirementGate.status ?? 'active';
|
|
133
|
+
const gateSuffix = result.activeRequirementGate.relevance === 'background' ? '(仅背景提醒)' : '';
|
|
134
|
+
console.log(`当前处理阶段: ${gateStatus}${gateSuffix}`);
|
|
135
|
+
}
|
|
136
|
+
if (result.taskSummary) {
|
|
137
|
+
console.log(`后续任务进度: ${result.taskSummary.completed}/${result.taskSummary.total} 完成,${result.taskSummary.pending} 待处理,${result.taskSummary.blocked} 阻塞`);
|
|
138
|
+
if (result.taskSummary.implementation) {
|
|
139
|
+
console.log(`待落地任务: ${result.taskSummary.implementation.completed}/${result.taskSummary.implementation.total} 完成,${result.taskSummary.implementation.pending} 待处理`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (result.discovery) {
|
|
143
|
+
console.log(`调研进度: ${result.discovery.runId} 已覆盖 ${result.discovery.summary.covered}/${result.discovery.summary.total},待处理 ${result.discovery.summary.pending}`);
|
|
144
|
+
}
|
|
145
|
+
printKnowledgeSkillMatches(result.knowledgeSkills);
|
|
146
|
+
console.log('对外表达: 面向用户时,请优先说“本次调整”“后续任务”“继续落地”“完成后检查”这类人话,不要直接复述内部编号、命令、路径、版本号或流程术语。');
|
|
147
|
+
console.log(`建议下一步: ${result.recommendation.title}`);
|
|
148
|
+
if (result.recommendation.executionMode) {
|
|
149
|
+
console.log(`推进方式: ${labelExecutionMode(result.recommendation.executionMode)}`);
|
|
150
|
+
}
|
|
151
|
+
if (result.recommendation.parallelPlan?.eligible) {
|
|
152
|
+
console.log(`协作建议: ${result.recommendation.parallelPlan.summary}`);
|
|
153
|
+
}
|
|
154
|
+
console.log(`这样安排的原因: ${result.recommendation.reason}`);
|
|
155
|
+
if (result.recommendation.preparationCommand || result.recommendation.executionCommand || result.recommendation.commitCommand) {
|
|
156
|
+
console.log('开始动手前提: 只有在用户明确要求继续落地、实现、修复、深挖或提交时,才继续往下做;如果还缺这一步,就先用人话说明范围和影响。');
|
|
157
|
+
}
|
|
158
|
+
printExecutionConfirmationChecklist(result.recommendation.executionConfirmationChecklist);
|
|
159
|
+
if (result.recommendation.preparationCommand) {
|
|
160
|
+
console.log(`内部准备参考: ${result.recommendation.preparationCommand}`);
|
|
161
|
+
}
|
|
162
|
+
if (result.recommendation.executionCommand) {
|
|
163
|
+
console.log(`内部执行参考: ${result.recommendation.executionCommand}`);
|
|
164
|
+
}
|
|
165
|
+
if (result.recommendation.commitCommand) {
|
|
166
|
+
console.log(`内部提交参考: ${result.recommendation.commitCommand}`);
|
|
167
|
+
}
|
|
168
|
+
if (result.recommendation.loop?.worktreeRecommended) {
|
|
169
|
+
console.log('环境建议: 最好放到单独环境里继续,避免和别的事项串线。');
|
|
170
|
+
}
|
|
171
|
+
console.log(`内部检查参考: ${result.recommendation.verifyCommand}`);
|
|
172
|
+
console.log(`内部状态参考: ${result.files.runState}`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function printLoopResult(result, json) {
|
|
176
|
+
if (json) {
|
|
177
|
+
console.log(JSON.stringify(result, null, 2));
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (result.action === 'loop-prompt') {
|
|
182
|
+
console.log(`OpenPrd loop 提示词: ${result.ok ? '就绪' : '阻塞'}`);
|
|
183
|
+
if (result.task) {
|
|
184
|
+
console.log(`任务: ${result.task.id} ${result.task.title}`);
|
|
185
|
+
if (result.task.taskHandle) console.log(`任务句柄: ${result.task.taskHandle}`);
|
|
186
|
+
}
|
|
187
|
+
if (result.promptPath) {
|
|
188
|
+
console.log(`提示词: ${result.promptPath}`);
|
|
189
|
+
}
|
|
190
|
+
if (result.invocation?.display) {
|
|
191
|
+
console.log(`执行: ${result.invocation.display}`);
|
|
192
|
+
}
|
|
193
|
+
if (result.errors?.length) {
|
|
194
|
+
for (const error of result.errors) console.log(`- ${error}`);
|
|
195
|
+
}
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (result.action === 'loop-run') {
|
|
200
|
+
console.log(`OpenPrd loop 运行: ${result.ok ? '通过' : '失败'}${result.dryRun ? ' (dry-run)' : ''}`);
|
|
201
|
+
if (result.task) console.log(`任务: ${result.task.id} ${result.task.title}`);
|
|
202
|
+
if (result.task?.taskHandle) console.log(`任务句柄: ${result.task.taskHandle}`);
|
|
203
|
+
if (result.promptPath) console.log(`提示词: ${result.promptPath}`);
|
|
204
|
+
if (result.invocation?.display) console.log(`执行: ${result.invocation.display}`);
|
|
205
|
+
if (result.codexRuntime || result.preflight) {
|
|
206
|
+
printCodexRuntimeResult(result.codexRuntime ?? {
|
|
207
|
+
ok: result.preflight.ok,
|
|
208
|
+
preflight: result.preflight,
|
|
209
|
+
repair: result.repair,
|
|
210
|
+
repairAttempted: Boolean(result.repairAttempted),
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
if (result.finish?.commit) {
|
|
214
|
+
console.log(`提交: ${result.finish.commit.skipped ? '跳过' : result.finish.commit.sha}`);
|
|
215
|
+
}
|
|
216
|
+
if (result.finish?.projectRelease?.version) {
|
|
217
|
+
console.log(`项目版本: ${result.finish.projectRelease.version}`);
|
|
218
|
+
if (result.finish.projectRelease.tag?.tagName) {
|
|
219
|
+
const localSha = result.finish.projectRelease.tag.localSha ? ` -> ${result.finish.projectRelease.tag.localSha}` : '';
|
|
220
|
+
console.log(`版本 tag: ${result.finish.projectRelease.tag.tagName}${localSha}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (result.finish?.testReport) {
|
|
224
|
+
console.log(`测试报告: ${result.finish.testReport}`);
|
|
225
|
+
}
|
|
226
|
+
if (result.errors?.length) {
|
|
227
|
+
for (const error of result.errors) console.log(`- ${error}`);
|
|
228
|
+
}
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (result.action === 'loop-finish') {
|
|
233
|
+
console.log(`OpenPrd loop finish: ${result.ok ? '通过' : '失败'}`);
|
|
234
|
+
if (result.task) console.log(`任务: ${result.task.id} ${result.task.title}`);
|
|
235
|
+
if (result.task?.taskHandle) console.log(`任务句柄: ${result.task.taskHandle}`);
|
|
236
|
+
if (result.commit) console.log(`提交: ${result.commit.skipped ? '跳过' : result.commit.sha}`);
|
|
237
|
+
if (result.projectRelease?.version) {
|
|
238
|
+
console.log(`项目版本: ${result.projectRelease.version}`);
|
|
239
|
+
if (result.projectRelease.tag?.tagName) {
|
|
240
|
+
const localSha = result.projectRelease.tag.localSha ? ` -> ${result.projectRelease.tag.localSha}` : '';
|
|
241
|
+
console.log(`版本 tag: ${result.projectRelease.tag.tagName}${localSha}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (result.testReport) console.log(`测试报告: ${result.testReport}`);
|
|
245
|
+
if (result.learningReview) {
|
|
246
|
+
if (result.learningReview.skipped) {
|
|
247
|
+
console.log(`复盘学习包: 已跳过 (${result.learningReview.reason})`);
|
|
248
|
+
} else if (result.learningReview.ok === false) {
|
|
249
|
+
console.log(`复盘学习包: 生成失败 (${result.learningReview.errors?.[0] ?? 'unknown'})`);
|
|
250
|
+
} else {
|
|
251
|
+
console.log(`复盘学习包: ${result.learningReview.packageId}`);
|
|
252
|
+
console.log(`HTML: ${result.learningReview.packagePaths?.readerHtml ?? '无'}`);
|
|
253
|
+
console.log(`题材: ${result.learningReview.genre?.label ?? '未知'}`);
|
|
254
|
+
if (result.learningReview.packageMeta?.styleLabel) console.log(`子风格: ${result.learningReview.packageMeta.styleLabel}`);
|
|
255
|
+
if (result.learningReview.packageMeta?.authoringStatus) console.log(`写作状态: ${result.learningReview.packageMeta.authoringStatus}`);
|
|
256
|
+
if (result.learningReview.packagePaths?.agentPrompt) console.log(`Agent 写作提示: ${result.learningReview.packagePaths.agentPrompt}`);
|
|
257
|
+
console.log(`已打开: ${result.learningReview.opened ? '是' : '否'}`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
printKnowledgeReview(result.knowledgeReview);
|
|
261
|
+
if (result.next) {
|
|
262
|
+
console.log(`下一任务: ${result.next.id} ${result.next.title}`);
|
|
263
|
+
if (result.next.taskHandle) console.log(`下一任务句柄: ${result.next.taskHandle}`);
|
|
264
|
+
}
|
|
265
|
+
if (result.projectRelease?.warnings?.length) {
|
|
266
|
+
for (const warning of result.projectRelease.warnings) {
|
|
267
|
+
console.log(`- ${warning}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (result.errors?.length) {
|
|
271
|
+
for (const error of result.errors) console.log(`- ${error}`);
|
|
272
|
+
}
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
console.log(`OpenPrd loop: ${result.action} ${result.ok ? '通过' : '失败'}`);
|
|
277
|
+
if (result.changeId) console.log(`变更: ${result.changeId}`);
|
|
278
|
+
if (result.summary) {
|
|
279
|
+
console.log(`任务: ${result.summary.done}/${result.summary.total} 完成,${result.summary.pending} 待处理,${result.summary.failed} 失败,${result.summary.blocked} 阻塞`);
|
|
280
|
+
}
|
|
281
|
+
if (result.next) {
|
|
282
|
+
console.log(`下一任务: ${result.next.id} ${result.next.title}`);
|
|
283
|
+
if (result.next.taskHandle) console.log(`下一任务句柄: ${result.next.taskHandle}`);
|
|
284
|
+
}
|
|
285
|
+
if (result.files) {
|
|
286
|
+
console.log(`任务清单: ${result.files.featureList}`);
|
|
287
|
+
}
|
|
288
|
+
if (result.errors?.length) {
|
|
289
|
+
for (const error of result.errors) console.log(`- ${error}`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export {
|
|
294
|
+
printExecutionConfirmationChecklist,
|
|
295
|
+
printRunResult,
|
|
296
|
+
printLoopResult,
|
|
297
|
+
};
|