@openprd/cli 0.1.11 → 0.1.19
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/changes/adaptive-project-framing/.openprd.yaml +2 -0
- package/.openprd/changes/adaptive-project-framing/design.md +47 -0
- package/.openprd/changes/adaptive-project-framing/proposal.md +35 -0
- package/.openprd/changes/adaptive-project-framing/specs/agent-requirements/spec.md +16 -0
- package/.openprd/changes/adaptive-project-framing/task-events.jsonl +20 -0
- package/.openprd/changes/adaptive-project-framing/tasks.md +340 -0
- package/.openprd/changes/docs-anti-pollution-and-design-direction-review/.openprd.yaml +2 -0
- package/.openprd/changes/docs-anti-pollution-and-design-direction-review/design.md +34 -0
- package/.openprd/changes/docs-anti-pollution-and-design-direction-review/proposal.md +23 -0
- package/.openprd/changes/docs-anti-pollution-and-design-direction-review/specs/agent-requirements/spec.md +16 -0
- package/.openprd/changes/docs-anti-pollution-and-design-direction-review/task-events.jsonl +14 -0
- package/.openprd/changes/docs-anti-pollution-and-design-direction-review/tasks.md +238 -0
- package/.openprd/changes/loop-isolated-worktree-commit/.openprd.yaml +2 -0
- package/.openprd/changes/loop-isolated-worktree-commit/design.md +53 -0
- package/.openprd/changes/loop-isolated-worktree-commit/proposal.md +38 -0
- package/.openprd/changes/loop-isolated-worktree-commit/specs/agent-requirements/spec.md +16 -0
- package/.openprd/changes/loop-isolated-worktree-commit/task-events.jsonl +22 -0
- package/.openprd/changes/loop-isolated-worktree-commit/tasks.md +357 -0
- package/.openprd/changes/openprd/.openprd.yaml +2 -0
- package/.openprd/changes/openprd/design.md +46 -0
- package/.openprd/changes/openprd/proposal.md +36 -0
- package/.openprd/changes/openprd/specs/agent-requirements/spec.md +16 -0
- package/.openprd/changes/openprd/task-events.jsonl +23 -0
- package/.openprd/changes/openprd/tasks.md +242 -0
- package/.openprd/changes/openprd-generated-change/.openprd.yaml +2 -0
- package/.openprd/changes/openprd-generated-change/design.md +50 -0
- package/.openprd/changes/openprd-generated-change/proposal.md +37 -0
- package/.openprd/changes/openprd-generated-change/specs/agent-requirements/spec.md +16 -0
- package/.openprd/changes/openprd-generated-change/task-events.jsonl +21 -0
- package/.openprd/changes/openprd-generated-change/tasks.md +357 -0
- package/.openprd/changes/pm-human-in-the-loop/.openprd.yaml +2 -0
- package/.openprd/changes/pm-human-in-the-loop/design.md +38 -0
- package/.openprd/changes/pm-human-in-the-loop/proposal.md +29 -0
- package/.openprd/changes/pm-human-in-the-loop/specs/agent-requirements/spec.md +16 -0
- package/.openprd/changes/pm-human-in-the-loop/task-events.jsonl +18 -0
- package/.openprd/changes/pm-human-in-the-loop/tasks.md +306 -0
- package/.openprd/changes/test-strategy-router/.openprd.yaml +2 -0
- package/.openprd/changes/test-strategy-router/design.md +65 -0
- package/.openprd/changes/test-strategy-router/proposal.md +45 -0
- package/.openprd/changes/test-strategy-router/specs/agent-requirements/spec.md +16 -0
- package/.openprd/changes/test-strategy-router/task-events.jsonl +1 -0
- package/.openprd/changes/test-strategy-router/tasks.md +132 -0
- package/.openprd/changes/verify-boundaries-and-historical-refresh/.openprd.yaml +2 -0
- package/.openprd/changes/verify-boundaries-and-historical-refresh/design.md +28 -0
- package/.openprd/changes/verify-boundaries-and-historical-refresh/proposal.md +23 -0
- package/.openprd/changes/verify-boundaries-and-historical-refresh/specs/agent-requirements/spec.md +16 -0
- package/.openprd/changes/verify-boundaries-and-historical-refresh/task-events.jsonl +11 -0
- package/.openprd/changes/verify-boundaries-and-historical-refresh/tasks.md +66 -0
- package/.openprd/changes/vibe-coding-dev-check/.openprd.yaml +2 -0
- package/.openprd/changes/vibe-coding-dev-check/design.md +37 -0
- package/.openprd/changes/vibe-coding-dev-check/proposal.md +28 -0
- package/.openprd/changes/vibe-coding-dev-check/specs/agent-requirements/spec.md +16 -0
- package/.openprd/changes/vibe-coding-dev-check/task-events.jsonl +18 -0
- package/.openprd/changes/vibe-coding-dev-check/tasks.md +306 -0
- package/.openprd/design/README.md +11 -6
- package/.openprd/design/README_EN.md +4 -1
- package/.openprd/design/active/asset-spec.md +6 -0
- package/.openprd/design/active/direction-plan.md +5 -5
- package/.openprd/design/active/selected-direction.md +2 -0
- package/.openprd/design/anti-slop.md +26 -1
- package/.openprd/design/assets/surface-presets.md +6 -0
- package/.openprd/design/checklists/ui-quality-gate.md +7 -0
- package/.openprd/design/lenses/frontend-lenses.md +28 -0
- package/.openprd/design/recipes/content-experience.md +8 -1
- package/.openprd/design/recipes/operational-tool.md +7 -0
- package/.openprd/design/recipes/product-launch.md +8 -1
- package/.openprd/design/templates/README.md +6 -3
- package/.openprd/design/templates/README_EN.md +6 -3
- package/.openprd/design/templates/ops-dashboard.html +8 -1
- package/.openprd/design/templates/product-launch.html +2 -1
- package/.openprd/design/themes/theme-catalog.json +23 -5
- package/.openprd/engagements/active/prd.md +137 -112
- package/.openprd/standards/config.json +7 -1
- package/AGENTS.md +13 -3
- package/README.md +127 -14
- package/README_EN.md +156 -13
- package/package.json +1 -1
- package/scripts/dev-check-wrapup-copy.mjs +46 -21
- package/skills/openprd-diagram-review/SKILL.md +27 -2
- package/skills/openprd-diagram-review/references/explanation-svg-patterns.md +131 -0
- package/skills/openprd-discovery-loop/SKILL.md +2 -2
- package/skills/openprd-frontend-design/SKILL.md +44 -20
- package/skills/openprd-frontend-design/references/design-asset-contract.md +6 -0
- package/skills/openprd-frontend-design/references/direction-engine.md +29 -4
- package/skills/openprd-harness/SKILL.md +22 -16
- package/skills/openprd-quality/SKILL.md +10 -8
- package/skills/openprd-requirement-intake/SKILL.md +2 -0
- package/skills/openprd-router/SKILL.md +2 -1
- package/skills/openprd-shared/SKILL.md +26 -16
- package/skills/openprd-standards/SKILL.md +4 -2
- package/src/agent-canonical-content.js +810 -0
- package/src/agent-integration.js +258 -777
- package/src/canvas-app-client-app.js +838 -0
- package/src/canvas-app-client-helpers.js +1138 -0
- package/src/canvas-app-styles.js +898 -0
- package/src/canvas-app.html.js +135 -0
- package/src/canvas-binding.js +414 -0
- package/src/canvas-codex-relay.js +430 -0
- package/src/canvas-i18n.js +282 -0
- package/src/canvas-scene.js +377 -0
- package/src/canvas-session-store.js +148 -0
- package/src/canvas-workspace.js +1264 -0
- package/src/cli/args.js +72 -4
- package/src/cli/basic-print.js +3 -0
- package/src/cli/canvas-print.js +106 -0
- package/src/cli/doctor-print.js +20 -0
- package/src/cli/print.js +2 -0
- package/src/cli/quality-commands.js +222 -0
- package/src/cli/quality-print.js +28 -2
- package/src/cli/run-print.js +5 -0
- package/src/cli/workflow-print.js +3 -0
- package/src/codex-app-server-relay.js +251 -0
- package/src/codex-hook-runner-template.mjs +978 -58
- package/src/design-starter-support.js +2 -2
- package/src/design-starter.js +39 -3
- package/src/dev-standards.js +125 -10
- package/src/diagram-core.js +6 -20
- package/src/discovery.js +2 -8
- package/src/docs-compact.js +125 -0
- package/src/execution-strategy.js +1 -1
- package/src/fleet.js +9 -3
- package/src/guide.js +227 -0
- package/src/html-artifacts.js +428 -7
- package/src/knowledge.js +45 -11
- package/src/language-policy.js +63 -3
- package/src/learning-review.js +1 -1
- package/src/loop.js +1 -1
- package/src/openprd.js +68 -131
- package/src/openspec/change-validate.js +2 -2
- package/src/openspec/constants.js +6 -7
- package/src/openspec/execute.js +2 -2
- package/src/openspec/generate.js +318 -102
- package/src/openspec/migration.js +168 -0
- package/src/openspec/paths.js +1 -22
- package/src/prd-core.js +1 -1
- package/src/quality-visual-review.js +257 -14
- package/src/quality.js +46 -8
- package/src/review-presentation.js +60 -0
- package/src/run-harness.js +41 -3
- package/src/session-evidence.js +363 -0
- package/src/standards.js +64 -0
- package/src/test-strategy.js +7 -4
- package/src/visual-compare-centering.js +622 -0
- package/src/visual-compare-core.js +1123 -0
- package/src/visual-compare.js +620 -698
- package/src/workspace-core.js +94 -4
- package/src/workspace-workflow.js +46 -1
package/src/fleet.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { cjoin, exists, writeJson } from './fs-utils.js';
|
|
4
|
+
import { hasLegacyOpenPrdArtifacts } from './openspec/migration.js';
|
|
4
5
|
import { SOURCE_INVENTORY_IGNORE_DIRS } from './source-inventory.js';
|
|
5
6
|
import { timestamp } from './time.js';
|
|
6
7
|
import { readWorkspaceRegistry, upsertWorkspaceRegistryEntry } from './workspace-registry.js';
|
|
@@ -33,6 +34,7 @@ const FLEET_PROJECT_MARKERS = [
|
|
|
33
34
|
'Makefile',
|
|
34
35
|
];
|
|
35
36
|
const FLEET_AGENT_MARKERS = ['.codex', '.claude', '.cursor', 'AGENTS.md', 'CLAUDE.md'];
|
|
37
|
+
const FLEET_LEGACY_OPENPRD_MARKER = 'openprd-legacy-artifacts';
|
|
36
38
|
|
|
37
39
|
function normalizeCsvList(value) {
|
|
38
40
|
if (!value) {
|
|
@@ -73,7 +75,7 @@ function pathWithinRoot(rootPath, candidatePath) {
|
|
|
73
75
|
}
|
|
74
76
|
|
|
75
77
|
function classifyFleetMarkers(markers) {
|
|
76
|
-
if (markers.includes('.openprd')) {
|
|
78
|
+
if (markers.includes('.openprd') || markers.includes(FLEET_LEGACY_OPENPRD_MARKER)) {
|
|
77
79
|
return 'openprd-workspace';
|
|
78
80
|
}
|
|
79
81
|
if (markers.some((marker) => FLEET_AGENT_MARKERS.includes(marker))) {
|
|
@@ -107,7 +109,11 @@ function plannedFleetAction(category, options) {
|
|
|
107
109
|
async function detectFleetMarkers(projectPath) {
|
|
108
110
|
const entries = await fs.readdir(projectPath, { withFileTypes: true }).catch(() => []);
|
|
109
111
|
const names = new Set(entries.map((entry) => entry.name));
|
|
110
|
-
|
|
112
|
+
const markers = FLEET_PROJECT_MARKERS.filter((marker) => names.has(marker));
|
|
113
|
+
if (!markers.includes('.openprd') && await hasLegacyOpenPrdArtifacts(projectPath)) {
|
|
114
|
+
markers.push(FLEET_LEGACY_OPENPRD_MARKER);
|
|
115
|
+
}
|
|
116
|
+
return markers;
|
|
111
117
|
}
|
|
112
118
|
|
|
113
119
|
async function scanFleetProjects(rootPath, options = {}) {
|
|
@@ -199,7 +205,7 @@ async function collectRegistryFleetScope(rootPath, options, registry) {
|
|
|
199
205
|
path: entry.workspaceRoot,
|
|
200
206
|
relativePath: path.relative(root, entry.workspaceRoot) || '.',
|
|
201
207
|
category: 'openprd-workspace',
|
|
202
|
-
markers: ['.openprd'],
|
|
208
|
+
markers: ['.openprd', ...(await hasLegacyOpenPrdArtifacts(entry.workspaceRoot) ? [FLEET_LEGACY_OPENPRD_MARKER] : [])],
|
|
203
209
|
discoverySource: 'registry',
|
|
204
210
|
registryKnown: true,
|
|
205
211
|
registryEntry: entry,
|
package/src/guide.js
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
const PLACEHOLDER_TEXT = /^待(补充|澄清|确认)/u;
|
|
5
|
+
|
|
6
|
+
export const GUIDE_STAGES = [
|
|
7
|
+
{
|
|
8
|
+
id: 'clarify',
|
|
9
|
+
title: '需求澄清',
|
|
10
|
+
description: '把要解决的问题、目标用户和这次的目标讲清楚。',
|
|
11
|
+
nextAction: '把想法直接告诉 Agent:要解决什么问题、给谁用、这次想达成什么。Agent 会用 openprd capture 记录下来。',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
id: 'draft',
|
|
15
|
+
title: '需求摘要',
|
|
16
|
+
description: '把已确认的信息整理成一份可评审的 PRD 草稿。',
|
|
17
|
+
nextAction: '让 Agent 运行 openprd synthesize,生成 PRD 草稿和评审页面。',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: 'review',
|
|
21
|
+
title: '评审确认',
|
|
22
|
+
description: '打开 review.html,逐块确认主流程、功能、护栏和风险。',
|
|
23
|
+
nextAction: '打开评审页面逐块看完,用底部按钮告诉 Agent「认可并继续」或「需要调整」。',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 'direction',
|
|
27
|
+
title: '交互方向选择',
|
|
28
|
+
description: '界面类需求会给出 2 到 4 个交互稿方向,选定一个再继续。',
|
|
29
|
+
nextAction: '在评审页面或画布里比较各方向,点「选这个方向」把选择发回 Agent。',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: 'tasks',
|
|
33
|
+
title: '任务拆解',
|
|
34
|
+
description: '把已确认的需求拆成一份带验证要求的任务清单。',
|
|
35
|
+
nextAction: '让 Agent 运行 openprd change . --generate 生成变更提案和任务清单。',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: 'build',
|
|
39
|
+
title: '实现执行',
|
|
40
|
+
description: 'Agent 按任务清单实现,每个任务完成后留下验证证据。',
|
|
41
|
+
nextAction: '让 Agent 按任务清单继续实现;你可以随时问「现在做到哪一步了」。',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: 'wrapup',
|
|
45
|
+
title: '验证收口',
|
|
46
|
+
description: '运行质量门禁,确认全部任务完成且验证通过。',
|
|
47
|
+
nextAction: '让 Agent 运行 openprd quality . --verify 和 openprd run . --verify 收口。',
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
async function readJson(filePath) {
|
|
52
|
+
const text = await fs.readFile(filePath, 'utf8').catch(() => null);
|
|
53
|
+
if (text === null) return null;
|
|
54
|
+
try {
|
|
55
|
+
return JSON.parse(text);
|
|
56
|
+
} catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function taskProgressFromMarkdown(text) {
|
|
62
|
+
const done = (text.match(/^\s*-\s*\[x\]/gim) ?? []).length;
|
|
63
|
+
const open = (text.match(/^\s*-\s*\[ \]/gim) ?? []).length;
|
|
64
|
+
return { done, total: done + open };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function latestQualityReadiness(projectRoot) {
|
|
68
|
+
const reportsDir = path.join(projectRoot, '.openprd', 'quality', 'reports');
|
|
69
|
+
const entries = await fs.readdir(reportsDir).catch(() => []);
|
|
70
|
+
const evalReports = entries.filter((name) => name.startsWith('eval-') && name.endsWith('.json')).sort();
|
|
71
|
+
if (evalReports.length === 0) return null;
|
|
72
|
+
const latest = await readJson(path.join(reportsDir, evalReports[evalReports.length - 1]));
|
|
73
|
+
return latest?.readiness ?? null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function guideWorkspace(projectRoot) {
|
|
77
|
+
const resolvedRoot = path.resolve(projectRoot);
|
|
78
|
+
const stateDir = path.join(resolvedRoot, '.openprd', 'state');
|
|
79
|
+
const current = await readJson(path.join(stateDir, 'current.json'));
|
|
80
|
+
const changesState = await readJson(path.join(stateDir, 'changes.json'));
|
|
81
|
+
|
|
82
|
+
const problemStatement = String(current?.problemStatement ?? '').trim();
|
|
83
|
+
const clarified = problemStatement.length > 0 && !PLACEHOLDER_TEXT.test(problemStatement);
|
|
84
|
+
|
|
85
|
+
const prdVersion = Number(current?.prdVersion ?? 0);
|
|
86
|
+
const drafted = clarified && prdVersion >= 1;
|
|
87
|
+
|
|
88
|
+
const reviewStatus = current?.reviewStatus ?? null;
|
|
89
|
+
const reviewConfirmed = drafted && reviewStatus?.status === 'confirmed';
|
|
90
|
+
|
|
91
|
+
let designDirections = null;
|
|
92
|
+
const versionId = reviewStatus?.versionId ?? null;
|
|
93
|
+
if (versionId) {
|
|
94
|
+
const snapshot = await readJson(path.join(stateDir, 'versions', `${versionId}.json`));
|
|
95
|
+
const block = snapshot?.reviewPresentation?.designDirections;
|
|
96
|
+
if (block && Array.isArray(block.directions) && block.directions.length > 0) {
|
|
97
|
+
designDirections = block.directions;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const directionApplicable = designDirections !== null;
|
|
101
|
+
const directionChosen = Boolean(reviewStatus?.designDirection?.id);
|
|
102
|
+
|
|
103
|
+
const activeChange = changesState?.activeChange ?? null;
|
|
104
|
+
let taskProgress = null;
|
|
105
|
+
if (activeChange) {
|
|
106
|
+
const tasksText = await fs.readFile(
|
|
107
|
+
path.join(resolvedRoot, '.openprd', 'changes', activeChange, 'tasks.md'),
|
|
108
|
+
'utf8',
|
|
109
|
+
).catch(() => null);
|
|
110
|
+
if (tasksText !== null) {
|
|
111
|
+
taskProgress = taskProgressFromMarkdown(tasksText);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const tasksReady = reviewConfirmed && taskProgress !== null && taskProgress.total > 0;
|
|
115
|
+
const buildDone = tasksReady && taskProgress.done === taskProgress.total;
|
|
116
|
+
|
|
117
|
+
const readiness = await latestQualityReadiness(resolvedRoot);
|
|
118
|
+
const wrappedUp = buildDone && readiness?.productionReady === true;
|
|
119
|
+
|
|
120
|
+
const completion = {
|
|
121
|
+
clarify: clarified,
|
|
122
|
+
draft: drafted,
|
|
123
|
+
review: reviewConfirmed,
|
|
124
|
+
direction: directionApplicable ? directionChosen : null,
|
|
125
|
+
tasks: tasksReady,
|
|
126
|
+
build: buildDone,
|
|
127
|
+
wrapup: wrappedUp,
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const stages = [];
|
|
131
|
+
let currentStage = null;
|
|
132
|
+
for (let index = 0; index < GUIDE_STAGES.length; index += 1) {
|
|
133
|
+
const spec = GUIDE_STAGES[index];
|
|
134
|
+
const completed = completion[spec.id];
|
|
135
|
+
let status;
|
|
136
|
+
if (spec.id === 'direction' && !directionApplicable) {
|
|
137
|
+
status = 'skipped';
|
|
138
|
+
} else if (completed) {
|
|
139
|
+
status = 'done';
|
|
140
|
+
} else if (currentStage === null) {
|
|
141
|
+
status = 'current';
|
|
142
|
+
currentStage = spec.id;
|
|
143
|
+
} else {
|
|
144
|
+
status = 'pending';
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
let detail = null;
|
|
148
|
+
if (spec.id === 'clarify' && clarified) {
|
|
149
|
+
detail = '问题定义已记录。';
|
|
150
|
+
} else if (spec.id === 'draft' && drafted) {
|
|
151
|
+
detail = `已生成第 ${prdVersion} 版需求摘要。`;
|
|
152
|
+
} else if (spec.id === 'review' && reviewStatus) {
|
|
153
|
+
detail = reviewConfirmed ? `评审版本 ${versionId} 已确认。` : `评审版本 ${versionId} 等待确认。`;
|
|
154
|
+
} else if (spec.id === 'direction') {
|
|
155
|
+
if (!directionApplicable) {
|
|
156
|
+
detail = '本次需求不涉及界面方向选择,自动跳过。';
|
|
157
|
+
} else if (directionChosen) {
|
|
158
|
+
detail = `已选定方向「${reviewStatus.designDirection.title ?? reviewStatus.designDirection.id}」。`;
|
|
159
|
+
} else {
|
|
160
|
+
detail = `${designDirections.length} 个交互方向等待选择。`;
|
|
161
|
+
}
|
|
162
|
+
} else if (spec.id === 'tasks' && activeChange && taskProgress) {
|
|
163
|
+
detail = `变更 ${activeChange} 已有 ${taskProgress.total} 个任务。`;
|
|
164
|
+
} else if (spec.id === 'build' && taskProgress) {
|
|
165
|
+
detail = `任务进度 ${taskProgress.done}/${taskProgress.total}。`;
|
|
166
|
+
} else if (spec.id === 'wrapup' && readiness && buildDone) {
|
|
167
|
+
detail = readiness.productionReady ? '质量门禁已通过。' : '质量门禁尚未全部通过。';
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
stages.push({
|
|
171
|
+
id: spec.id,
|
|
172
|
+
index: index + 1,
|
|
173
|
+
title: spec.title,
|
|
174
|
+
description: spec.description,
|
|
175
|
+
status,
|
|
176
|
+
detail,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (currentStage === null) {
|
|
181
|
+
currentStage = 'wrapup';
|
|
182
|
+
}
|
|
183
|
+
const currentSpec = GUIDE_STAGES.find((spec) => spec.id === currentStage);
|
|
184
|
+
const currentEntry = stages.find((stage) => stage.id === currentStage);
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
ok: true,
|
|
188
|
+
action: 'guide',
|
|
189
|
+
projectRoot: resolvedRoot,
|
|
190
|
+
totalStages: GUIDE_STAGES.length,
|
|
191
|
+
currentStage: {
|
|
192
|
+
id: currentStage,
|
|
193
|
+
index: currentEntry?.index ?? GUIDE_STAGES.length,
|
|
194
|
+
title: currentSpec?.title ?? '验证收口',
|
|
195
|
+
},
|
|
196
|
+
nextAction: currentEntry?.status === 'done' ? '全部阶段已完成,可以开始下一个需求。' : currentSpec?.nextAction ?? null,
|
|
197
|
+
reviewPath: reviewStatus?.entryPath ?? reviewStatus?.reviewPath ?? null,
|
|
198
|
+
stages,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function printGuideResult(result, json) {
|
|
203
|
+
if (json) {
|
|
204
|
+
console.log(JSON.stringify(result, null, 2));
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
console.log(`OpenPrd 流程指引: 第 ${result.currentStage.index}/${result.totalStages} 步 · ${result.currentStage.title}`);
|
|
208
|
+
for (const stage of result.stages) {
|
|
209
|
+
const marker = stage.status === 'done'
|
|
210
|
+
? '[完成]'
|
|
211
|
+
: stage.status === 'current'
|
|
212
|
+
? '[当前]'
|
|
213
|
+
: stage.status === 'skipped'
|
|
214
|
+
? '[跳过]'
|
|
215
|
+
: '[待办]';
|
|
216
|
+
console.log(`${marker} ${stage.index}. ${stage.title} — ${stage.description}`);
|
|
217
|
+
if (stage.detail) {
|
|
218
|
+
console.log(` ${stage.detail}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (result.nextAction) {
|
|
222
|
+
console.log(`下一步: ${result.nextAction}`);
|
|
223
|
+
}
|
|
224
|
+
if (result.reviewPath) {
|
|
225
|
+
console.log(`评审页面: ${result.reviewPath}`);
|
|
226
|
+
}
|
|
227
|
+
}
|