@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
package/src/openprd.js
CHANGED
|
@@ -1,33 +1,74 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* 核心功能
|
|
3
|
+
* 汇总 OpenPrd CLI 的命令入口、workspace 函数编排和测试可复用导出。
|
|
4
|
+
*
|
|
5
|
+
* 输入
|
|
6
|
+
* 接收 CLI 参数、项目路径、OpenPrd 工作区文件和各子模块 workspace 结果。
|
|
7
|
+
*
|
|
8
|
+
* 输出
|
|
9
|
+
* 执行 init/setup/update/doctor/run/loop 等命令,打印结果并导出内部 workspace API。
|
|
10
|
+
*
|
|
11
|
+
* 定位
|
|
12
|
+
* 位于 CLI 应用层,负责路由与组合,不承载单个领域的深层业务规则。
|
|
13
|
+
*
|
|
14
|
+
* 依赖
|
|
15
|
+
* 依赖 cli/args、cli/print、agent-integration、loop、standards、quality、openspec 等模块。
|
|
16
|
+
*
|
|
17
|
+
* 维护规则
|
|
18
|
+
* 新增命令或参数时同步更新 usage、打印契约、docs/basic/backend-structure.md 和相关测试。
|
|
19
|
+
*/
|
|
1
20
|
import fs from 'node:fs/promises';
|
|
2
21
|
import path from 'node:path';
|
|
3
22
|
import { analyzePrdSnapshot, buildPrdSnapshot, formatVersionId } from './prd-core.js';
|
|
23
|
+
import { formatTemplatePackDisplay } from './product-type-copy.js';
|
|
24
|
+
import { buildSnapshotChangeSummary } from './change-summary.js';
|
|
25
|
+
import { appendReleaseEntry, buildReleaseChangeSummary, buildReleaseLedgerSummary, loadReleaseLedger, saveReleaseLedger, setCurrentReleaseVersion, setReleaseLedgerEnabled, setReleaseVersionStatus } from './release-ledger.js';
|
|
4
26
|
import { validateOpenSpecChangeWorkspace } from './openspec/change-validate.js';
|
|
5
27
|
import { generateOpenSpecChangeWorkspace as writeOpenSpecChangeWorkspace } from './openspec/generate.js';
|
|
6
|
-
import { advanceOpenSpecTaskWorkspace, listOpenSpecTaskWorkspace, verifyOpenSpecTaskWorkspace } from './openspec/execute.js';
|
|
28
|
+
import { advanceOpenSpecTaskWorkspace, checkOpenSpecTaskEvidenceWorkspace, listOpenSpecTaskWorkspace, verifyOpenSpecTaskWorkspace } from './openspec/execute.js';
|
|
29
|
+
import { ensureCodexCliReady } from './codex-runtime.js';
|
|
7
30
|
import { activateOpenPrdChangeWorkspace, applyOpenPrdChangeWorkspace, archiveOpenPrdChangeWorkspace, closeOpenPrdChangeWorkspace, listAcceptedSpecsWorkspace, listOpenPrdChangesWorkspace } from './openspec/change-lifecycle.js';
|
|
8
31
|
import { checkStandardsWorkspace, classifyExternalReferenceWorkspace, initStandardsWorkspace } from './standards.js';
|
|
9
32
|
import { doctorOpenPrdAgentIntegration, setupOpenPrdAgentIntegration, updateOpenPrdAgentIntegration } from './agent-integration.js';
|
|
10
33
|
import { finishLoopWorkspace, initLoopWorkspace, nextLoopWorkspace, planLoopWorkspace, promptLoopWorkspace, runLoopWorkspace, statusLoopWorkspace, verifyLoopWorkspace } from './loop.js';
|
|
11
34
|
import { timestamp } from './time.js';
|
|
12
35
|
import { parseCommandArgs, usage } from './cli/args.js';
|
|
13
|
-
import { printAcceptedSpecsResult, printAgentIntegrationResult, printBenchmarkResult, printCaptureResult, printClarifyResult, printClassifyResult, printDevelopmentStandardsResult, printDiagramResult, printDiffResult, printDoctorResult, printFleetResult, printFreezeResult, printGrowthResult, printHandoffResult, printHistoryResult, printInitResult, printInterviewResult, printLearningResult, printLoopResult, printNextResult, printOpenPrdChangeActionResult, printOpenPrdChangesResult, printOpenSpecChangeValidationResult, printOpenSpecDiscoveryResult, printOpenSpecGenerateResult, printOpenSpecTaskResult, printPlaygroundResult, printQualityResult, printReviewResult, printRunResult, printSelfUpdateResult, printStandardsResult, printStatus, printSynthesizeResult, printUpgradeResult, printValidation, printVisualCompareResult } from './cli/print.js';
|
|
14
|
-
import { cjoin, exists, writeJson, writeText, writeYaml } from './fs-utils.js';
|
|
36
|
+
import { printAcceptedSpecsResult, printAgentIntegrationResult, printBenchmarkResult, printCaptureResult, printClarifyResult, printClassifyResult, printDevelopmentStandardsResult, printDiagramResult, printDiffResult, printDoctorResult, printFleetResult, printFreezeResult, printGrowthResult, printHandoffResult, printHistoryResult, printInitResult, printInterviewResult, printKnowledgeResult, printLearningResult, printLoopResult, printNextResult, printOpenPrdChangeActionResult, printOpenPrdChangesResult, printOpenSpecChangeValidationResult, printOpenSpecDiscoveryResult, printOpenSpecGenerateResult, printOpenSpecTaskResult, printPlaygroundResult, printQualityResult, printReleaseResult, printReviewResult, printRunResult, printSelfUpdateResult, printStandardsResult, printStatus, printSynthesizeResult, printUpgradeResult, printValidation, printVisualCompareResult } from './cli/print.js';
|
|
37
|
+
import { cjoin, exists, readJson, writeJson, writeText, writeYaml } from './fs-utils.js';
|
|
15
38
|
import { diagramWorkspace } from './diagram-workspace.js';
|
|
16
39
|
import { createOpenSpecDiscoveryWorkspace } from './discovery.js';
|
|
17
40
|
import { createFleetWorkspace } from './fleet.js';
|
|
18
41
|
import { selfUpdateWorkspace, upgradeWorkspace } from './self-update.js';
|
|
19
42
|
import { backfillWorkUnitsWorkspace } from './work-unit-migration.js';
|
|
20
43
|
import { generateLearningReviewWorkspace, setLearningReviewModeWorkspace } from './learning-review.js';
|
|
21
|
-
import { addBenchmarkWorkspace, approveBenchmarkWorkspace, benchmarkWorkspace, listBenchmarkWorkspace, verifyBenchmarkWorkspace } from './benchmark.js';
|
|
44
|
+
import { addBenchmarkWorkspace, approveBenchmarkWorkspace, benchmarkWorkspace, listBenchmarkWorkspace, observeBenchmarkSourceWorkspace, verifyBenchmarkWorkspace } from './benchmark.js';
|
|
22
45
|
import { initQualityWorkspace, qualityWorkspace, verifyQualityWorkspace, learnQualityWorkspace } from './quality.js';
|
|
46
|
+
import {
|
|
47
|
+
archiveKnowledgeCandidate,
|
|
48
|
+
listKnowledgeCandidates,
|
|
49
|
+
recordKnowledgeReviewSignal,
|
|
50
|
+
rejectKnowledgeCandidate,
|
|
51
|
+
restoreKnowledgeCandidate,
|
|
52
|
+
reviewKnowledgeWorkspace,
|
|
53
|
+
} from './knowledge.js';
|
|
23
54
|
import { createRunWorkspace } from './run-harness.js';
|
|
24
55
|
import { checkDevelopmentStandardsWorkspace } from './dev-standards.js';
|
|
25
|
-
import {
|
|
56
|
+
import {
|
|
57
|
+
applyGrowthCandidateWorkspace,
|
|
58
|
+
checkGrowthWorkspace,
|
|
59
|
+
initGrowthWorkspace,
|
|
60
|
+
recordGrowthCheckpointWorkspace,
|
|
61
|
+
rejectGrowthCandidateWorkspace,
|
|
62
|
+
reviewGrowthWorkspace,
|
|
63
|
+
} from './growth.js';
|
|
26
64
|
import { buildReviewPresentationTemplatePayload, reviewPresentationWorkspace } from './review-presentation.js';
|
|
65
|
+
import { analyzeWorkspaceRegistryHygiene } from './registry-hygiene.js';
|
|
27
66
|
import { syncSessionBindingFromChange } from './session-binding.js';
|
|
67
|
+
import { readSessionRegistry } from './session-registry.js';
|
|
28
68
|
import { visualCompareWorkspace } from './visual-compare.js';
|
|
29
69
|
import { captureWorkspace, clarifyWorkspace, classifyWorkspace, computeWorkspaceGuidance, diffWorkspace, historyWorkspace, interviewWorkspace, nextWorkspace, playgroundWorkspace, reviewWorkspace, synthesizeWorkspace } from './workspace-workflow.js';
|
|
30
|
-
import { appendDecision, appendProgress, appendVerification, appendWorkflowEvent, buildWorkflowTaskGraph, computeWorkspaceDigest, CORE_TEMPLATE_FILES, ensureWorkspaceSkeleton, isSupportedProductType, loadLatestVersionSnapshot, loadWorkspace, migrateWorkspaceSkeleton, normalizeVersionId, readVersionIndex, resolveActiveTemplatePack, resolveCurrentProductType, validateWorkspace } from './workspace-core.js';
|
|
70
|
+
import { appendDecision, appendProgress, appendVerification, appendWorkflowEvent, buildCurrentStateSnapshot, buildWorkflowTaskGraph, computeWorkspaceDigest, CORE_TEMPLATE_FILES, ensureWorkspaceSkeleton, isSupportedProductType, loadCurrentLaneSnapshot, loadLatestVersionSnapshot, loadWorkspace, migrateWorkspaceSkeleton, normalizeVersionId, persistWorkspaceCurrentState, readVersionIndex, resolveActiveTemplatePack, resolveCurrentProductType, validateWorkspace } from './workspace-core.js';
|
|
71
|
+
import { readWorkspaceRegistry } from './workspace-registry.js';
|
|
31
72
|
|
|
32
73
|
async function initWorkspace(projectRoot, options) {
|
|
33
74
|
const ws = await ensureWorkspaceSkeleton(projectRoot, options);
|
|
@@ -41,7 +82,9 @@ async function initWorkspace(projectRoot, options) {
|
|
|
41
82
|
action: 'init',
|
|
42
83
|
enableUserCodexConfig: Boolean(options.enableUserCodexConfig),
|
|
43
84
|
codexHome: options.codexHome,
|
|
85
|
+
cursorHome: options.cursorHome,
|
|
44
86
|
openprdHome: options.openprdHome,
|
|
87
|
+
platform: options.platform,
|
|
45
88
|
hookProfile: options.hookProfile,
|
|
46
89
|
});
|
|
47
90
|
const config = workspace.data.config ?? {};
|
|
@@ -86,7 +129,7 @@ async function initWorkspace(projectRoot, options) {
|
|
|
86
129
|
});
|
|
87
130
|
await appendProgress(workspace, [
|
|
88
131
|
`已初始化工作区: ${workspace.workspaceRoot}。`,
|
|
89
|
-
|
|
132
|
+
`场景模板: ${formatTemplatePackDisplay(currentState.templatePack, { fallback: '待确认' })}。`,
|
|
90
133
|
]);
|
|
91
134
|
|
|
92
135
|
return { ws: workspace, created: ws.created, currentState, standards, quality, growth, agentIntegration };
|
|
@@ -119,7 +162,9 @@ async function setupAgentIntegrationWorkspace(projectRoot, options = {}) {
|
|
|
119
162
|
action: 'setup',
|
|
120
163
|
enableUserCodexConfig: Boolean(options.enableUserCodexConfig),
|
|
121
164
|
codexHome: options.codexHome,
|
|
165
|
+
cursorHome: options.cursorHome,
|
|
122
166
|
openprdHome: options.openprdHome,
|
|
167
|
+
platform: options.platform,
|
|
123
168
|
hookProfile: options.hookProfile,
|
|
124
169
|
});
|
|
125
170
|
return { ...agentIntegration, initialized: false, migration, standards, quality, growth };
|
|
@@ -135,7 +180,9 @@ async function updateAgentIntegrationWorkspace(projectRoot, options = {}) {
|
|
|
135
180
|
force: Boolean(options.force),
|
|
136
181
|
enableUserCodexConfig: Boolean(options.enableUserCodexConfig),
|
|
137
182
|
codexHome: options.codexHome,
|
|
183
|
+
cursorHome: options.cursorHome,
|
|
138
184
|
openprdHome: options.openprdHome,
|
|
185
|
+
platform: options.platform,
|
|
139
186
|
hookProfile: options.hookProfile,
|
|
140
187
|
});
|
|
141
188
|
return { ...agentIntegration, migration, standards, quality, growth };
|
|
@@ -146,16 +193,31 @@ async function doctorWorkspace(projectRoot, options = {}) {
|
|
|
146
193
|
tools: options.tools ?? 'all',
|
|
147
194
|
enableUserCodexConfig: Boolean(options.enableUserCodexConfig),
|
|
148
195
|
codexHome: options.codexHome,
|
|
196
|
+
cursorHome: options.cursorHome,
|
|
149
197
|
hookProfile: options.hookProfile,
|
|
150
198
|
});
|
|
151
|
-
const
|
|
199
|
+
const codexRuntime = (agentIntegration.tools ?? []).includes('codex') && options.checkCodexRuntime
|
|
200
|
+
? await ensureCodexCliReady({
|
|
201
|
+
cwd: projectRoot,
|
|
202
|
+
repair: Boolean(options.fix),
|
|
203
|
+
runCommand: options.codexRunCommand,
|
|
204
|
+
packageManager: options.packageManager,
|
|
205
|
+
})
|
|
206
|
+
: null;
|
|
207
|
+
const standards = await checkStandardsWorkspace(projectRoot, {
|
|
208
|
+
sourceManuals: options.sourceManuals,
|
|
209
|
+
docsContent: options.docsContent,
|
|
210
|
+
}).catch((error) => ({
|
|
152
211
|
ok: false,
|
|
153
212
|
errors: [error instanceof Error ? error.message : String(error)],
|
|
154
213
|
warnings: [],
|
|
155
214
|
checks: [],
|
|
156
215
|
docsRoot: path.join('docs', 'basic'),
|
|
157
216
|
}));
|
|
158
|
-
const validation = await validateWorkspace(projectRoot
|
|
217
|
+
const validation = await validateWorkspace(projectRoot, {
|
|
218
|
+
sourceManuals: options.sourceManuals,
|
|
219
|
+
docsContent: options.docsContent,
|
|
220
|
+
})
|
|
159
221
|
.then(({ report }) => report)
|
|
160
222
|
.catch((error) => ({
|
|
161
223
|
valid: false,
|
|
@@ -163,20 +225,88 @@ async function doctorWorkspace(projectRoot, options = {}) {
|
|
|
163
225
|
warnings: [],
|
|
164
226
|
checks: [],
|
|
165
227
|
}));
|
|
228
|
+
const workspaceRegistry = await readWorkspaceRegistry({ openprdHome: options.openprdHome }).catch(() => null);
|
|
229
|
+
const sessionRegistry = await readSessionRegistry({ openprdHome: options.openprdHome }).catch(() => null);
|
|
230
|
+
const registryHygiene = workspaceRegistry
|
|
231
|
+
? analyzeWorkspaceRegistryHygiene(workspaceRegistry.entries)
|
|
232
|
+
: { ok: true, issues: [] };
|
|
233
|
+
const registryWarnings = [
|
|
234
|
+
...(registryHygiene.issues ?? []).map((issue) => `registry: ${issue.message}`),
|
|
235
|
+
...((workspaceRegistry?.staleEntries ?? []).map((entry) => `registry: stale workspace ${entry.workspaceRoot} (${entry.reason})`)),
|
|
236
|
+
...((sessionRegistry?.staleEntries ?? []).map((entry) => `session-registry: stale session ${entry.sessionId} (${entry.reason})`)),
|
|
237
|
+
];
|
|
238
|
+
const doctorOk = agentIntegration.ok && (codexRuntime?.ok ?? true) && standards.ok && validation.valid;
|
|
239
|
+
const latestQuality = await readJson(cjoin(projectRoot, '.openprd', 'quality', 'reports', 'latest.json')).catch(() => null);
|
|
240
|
+
const doctorSignal = doctorOk
|
|
241
|
+
? {
|
|
242
|
+
kind: 'doctor-green',
|
|
243
|
+
ok: true,
|
|
244
|
+
summary: 'doctor passed',
|
|
245
|
+
}
|
|
246
|
+
: null;
|
|
247
|
+
if (doctorSignal) {
|
|
248
|
+
await recordKnowledgeReviewSignal(projectRoot, doctorSignal).catch(() => null);
|
|
249
|
+
}
|
|
250
|
+
const growthCheckpoint = doctorSignal
|
|
251
|
+
? await recordGrowthCheckpointWorkspace(projectRoot, {
|
|
252
|
+
outcome: 'doctor-passed',
|
|
253
|
+
reason: 'doctor-post-completion',
|
|
254
|
+
}).catch((error) => ({
|
|
255
|
+
ok: false,
|
|
256
|
+
action: 'growth-checkpoint',
|
|
257
|
+
projectRoot,
|
|
258
|
+
recorded: false,
|
|
259
|
+
errors: [error instanceof Error ? error.message : String(error)],
|
|
260
|
+
}))
|
|
261
|
+
: {
|
|
262
|
+
ok: true,
|
|
263
|
+
action: 'growth-checkpoint',
|
|
264
|
+
projectRoot,
|
|
265
|
+
recorded: false,
|
|
266
|
+
skipped: true,
|
|
267
|
+
reason: 'doctor-not-green',
|
|
268
|
+
};
|
|
269
|
+
const knowledgeReview = doctorSignal
|
|
270
|
+
? await reviewKnowledgeWorkspace(projectRoot, {
|
|
271
|
+
from: latestQuality?.jsonPath ?? null,
|
|
272
|
+
signal: doctorSignal,
|
|
273
|
+
}).catch((error) => ({
|
|
274
|
+
ok: false,
|
|
275
|
+
action: 'quality-knowledge-review',
|
|
276
|
+
skipped: false,
|
|
277
|
+
errors: [error instanceof Error ? error.message : String(error)],
|
|
278
|
+
}))
|
|
279
|
+
: {
|
|
280
|
+
ok: true,
|
|
281
|
+
action: 'quality-knowledge-review',
|
|
282
|
+
skipped: true,
|
|
283
|
+
reason: 'doctor-not-green',
|
|
284
|
+
};
|
|
166
285
|
|
|
167
286
|
return {
|
|
168
|
-
ok:
|
|
287
|
+
ok: doctorOk,
|
|
169
288
|
action: 'doctor',
|
|
170
289
|
projectRoot,
|
|
171
290
|
tools: agentIntegration.tools,
|
|
172
291
|
agentIntegration,
|
|
292
|
+
codexRuntime,
|
|
173
293
|
standards,
|
|
174
294
|
validation,
|
|
295
|
+
registry: {
|
|
296
|
+
workspace: workspaceRegistry,
|
|
297
|
+
sessions: sessionRegistry,
|
|
298
|
+
hygiene: registryHygiene,
|
|
299
|
+
warnings: registryWarnings,
|
|
300
|
+
},
|
|
301
|
+
growthCheckpoint,
|
|
302
|
+
knowledgeReview,
|
|
175
303
|
errors: [
|
|
176
304
|
...agentIntegration.errors,
|
|
305
|
+
...(codexRuntime?.errors ?? []).map((error) => `codex-runtime: ${error}`),
|
|
177
306
|
...(standards.errors ?? []).map((error) => `standards: ${error}`),
|
|
178
307
|
...(validation.errors ?? []).map((error) => `validate: ${error}`),
|
|
179
308
|
],
|
|
309
|
+
warnings: registryWarnings,
|
|
180
310
|
};
|
|
181
311
|
}
|
|
182
312
|
|
|
@@ -236,7 +366,7 @@ async function freezeWorkspace(projectRoot) {
|
|
|
236
366
|
}
|
|
237
367
|
|
|
238
368
|
let ws = validation.ws;
|
|
239
|
-
let latest = await
|
|
369
|
+
let latest = await loadCurrentLaneSnapshot(ws, { fallbackToLatest: true });
|
|
240
370
|
if (!latest) {
|
|
241
371
|
const synthesized = await synthesizeWorkspace(projectRoot, {});
|
|
242
372
|
ws = synthesized.ws;
|
|
@@ -294,28 +424,26 @@ async function freezeWorkspace(projectRoot) {
|
|
|
294
424
|
frozenAt: snapshot.frozenAt,
|
|
295
425
|
digest,
|
|
296
426
|
};
|
|
297
|
-
await
|
|
427
|
+
const storedCurrentState = await persistWorkspaceCurrentState(ws, currentState);
|
|
298
428
|
await appendWorkflowEvent(ws, 'frozen', {
|
|
299
429
|
versionId: snapshot.latestVersionId,
|
|
300
430
|
digest,
|
|
301
431
|
});
|
|
302
432
|
await appendVerification(ws, [
|
|
303
|
-
'
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
`PRD 版本: ${snapshot.prdVersion}`,
|
|
433
|
+
'定稿前检查通过。',
|
|
434
|
+
`本次确认稿: ${snapshot.latestVersionId}`,
|
|
435
|
+
`需求稿版本序号: ${snapshot.prdVersion}`,
|
|
307
436
|
]);
|
|
308
437
|
await appendProgress(ws, [
|
|
309
|
-
|
|
310
|
-
`Digest: ${digest}`,
|
|
438
|
+
`这版需求已经定稿。`,
|
|
311
439
|
]);
|
|
312
440
|
await appendDecision(ws, [
|
|
313
|
-
|
|
441
|
+
'这版需求已经定稿。',
|
|
314
442
|
`已准备好交接给 ${resolveActiveTemplatePack(ws) === 'base' ? '下游执行方' : '执行系统'}。`,
|
|
315
443
|
]);
|
|
316
|
-
await writeJson(ws.paths.taskGraph, buildWorkflowTaskGraph(
|
|
444
|
+
await writeJson(ws.paths.taskGraph, buildWorkflowTaskGraph(storedCurrentState));
|
|
317
445
|
|
|
318
|
-
return { ok: true, ws, report, snapshot, latest };
|
|
446
|
+
return { ok: true, ws: { ...ws, data: { ...ws.data, currentState: storedCurrentState } }, report, snapshot, latest };
|
|
319
447
|
}
|
|
320
448
|
|
|
321
449
|
async function handoffWorkspace(projectRoot, target) {
|
|
@@ -325,13 +453,23 @@ async function handoffWorkspace(projectRoot, target) {
|
|
|
325
453
|
}
|
|
326
454
|
|
|
327
455
|
const { ws, snapshot } = freeze;
|
|
456
|
+
const sourceSnapshot = freeze.latest?.snapshot ?? snapshot;
|
|
328
457
|
const exportDir = cjoin(ws.paths.exportsDir, target);
|
|
458
|
+
const releaseState = await loadReleaseLedger(projectRoot);
|
|
459
|
+
const releaseSummary = buildReleaseLedgerSummary(releaseState.ledger);
|
|
460
|
+
const releaseChangeSummary = releaseSummary.enabled && releaseSummary.currentVersion
|
|
461
|
+
? buildReleaseChangeSummary(releaseState.ledger, { limit: 5 })
|
|
462
|
+
: null;
|
|
463
|
+
const changeSummary = releaseChangeSummary?.items?.length
|
|
464
|
+
? releaseChangeSummary
|
|
465
|
+
: buildSnapshotChangeSummary(sourceSnapshot, { limit: 5 });
|
|
329
466
|
await fs.mkdir(exportDir, { recursive: true });
|
|
330
467
|
|
|
331
468
|
const handoff = {
|
|
332
469
|
version: 1,
|
|
333
470
|
versionId: snapshot.latestVersionId,
|
|
334
471
|
versionNumber: snapshot.prdVersion,
|
|
472
|
+
projectVersion: releaseSummary.currentVersion,
|
|
335
473
|
target,
|
|
336
474
|
generatedAt: timestamp(),
|
|
337
475
|
workspaceRoot: ws.workspaceRoot,
|
|
@@ -341,6 +479,10 @@ async function handoffWorkspace(projectRoot, target) {
|
|
|
341
479
|
productTypes: ws.data.config?.supportedProductTypes ?? [],
|
|
342
480
|
productType: resolveCurrentProductType(ws),
|
|
343
481
|
digest: snapshot.digest,
|
|
482
|
+
projectRelease: releaseSummary,
|
|
483
|
+
changeSummarySource: releaseChangeSummary?.items?.length ? 'release-ledger' : 'snapshot',
|
|
484
|
+
changeSummary,
|
|
485
|
+
releaseNotes: changeSummary.items.map((item) => item.sentence),
|
|
344
486
|
sourceFiles: [
|
|
345
487
|
...CORE_TEMPLATE_FILES,
|
|
346
488
|
...((await exists(ws.paths.activeArchitectureDiagramHtml)) ? ['engagements/active/architecture-diagram.html'] : []),
|
|
@@ -356,7 +498,12 @@ async function handoffWorkspace(projectRoot, target) {
|
|
|
356
498
|
};
|
|
357
499
|
|
|
358
500
|
await writeJson(cjoin(exportDir, 'handoff.json'), handoff);
|
|
359
|
-
|
|
501
|
+
const summarySection = handoff.changeSummary.markdown
|
|
502
|
+
? `\n## 变化摘要\n\n${handoff.changeSummary.markdown}\n`
|
|
503
|
+
: '';
|
|
504
|
+
const handoffMarkdown = `# 交接\n\n- 交接去向: ${target}\n${handoff.projectVersion ? `- 项目版本: ${handoff.projectVersion}\n` : ''}- 使用格式: ${handoff.schema}\n- 场景模板: ${formatTemplatePackDisplay(handoff.templatePack, { fallback: '待确认' })}\n- 下一步: ${handoff.nextStep}\n${summarySection}`;
|
|
505
|
+
await writeText(cjoin(exportDir, 'handoff.md'), handoffMarkdown);
|
|
506
|
+
await writeText(ws.paths.activeHandoff, handoffMarkdown);
|
|
360
507
|
if (await exists(ws.paths.activeArchitectureDiagramHtml)) {
|
|
361
508
|
await fs.copyFile(ws.paths.activeArchitectureDiagramHtml, cjoin(exportDir, 'architecture-diagram.html'));
|
|
362
509
|
}
|
|
@@ -394,10 +541,107 @@ async function handoffWorkspace(projectRoot, target) {
|
|
|
394
541
|
handedOffAt: handoff.generatedAt,
|
|
395
542
|
handoffTarget: target,
|
|
396
543
|
};
|
|
397
|
-
await
|
|
398
|
-
await writeJson(ws.paths.taskGraph, buildWorkflowTaskGraph(
|
|
544
|
+
const storedCurrentState = await persistWorkspaceCurrentState(ws, currentState);
|
|
545
|
+
await writeJson(ws.paths.taskGraph, buildWorkflowTaskGraph(storedCurrentState));
|
|
399
546
|
|
|
400
|
-
return { ok: true, ws, report: freeze.report, snapshot, handoff, exportDir };
|
|
547
|
+
return { ok: true, ws: { ...ws, data: { ...ws.data, currentState: storedCurrentState } }, report: freeze.report, snapshot, handoff, exportDir };
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
async function releaseWorkspace(projectRoot, options = {}) {
|
|
551
|
+
const workspaceRoot = cjoin(projectRoot, '.openprd');
|
|
552
|
+
if (!(await exists(workspaceRoot))) {
|
|
553
|
+
return {
|
|
554
|
+
ok: false,
|
|
555
|
+
action: 'release',
|
|
556
|
+
projectRoot,
|
|
557
|
+
errors: [`Missing workspace: ${workspaceRoot}. Please run openprd init first.`],
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
const loaded = await loadReleaseLedger(projectRoot);
|
|
562
|
+
let ledger = loaded.ledger;
|
|
563
|
+
const actions = [];
|
|
564
|
+
const warnings = [];
|
|
565
|
+
let changed = false;
|
|
566
|
+
|
|
567
|
+
try {
|
|
568
|
+
if (options.disable) {
|
|
569
|
+
({ ledger } = setReleaseLedgerEnabled(ledger, false));
|
|
570
|
+
actions.push('disable');
|
|
571
|
+
changed = true;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
if (options.enable) {
|
|
575
|
+
({ ledger } = setReleaseLedgerEnabled(ledger, true));
|
|
576
|
+
actions.push('enable');
|
|
577
|
+
changed = true;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (options.setVersion) {
|
|
581
|
+
const updated = setCurrentReleaseVersion(ledger, options.setVersion, {
|
|
582
|
+
status: options.status === 'released' ? 'current' : (options.status ?? 'current'),
|
|
583
|
+
});
|
|
584
|
+
ledger = updated.ledger;
|
|
585
|
+
actions.push('set-version');
|
|
586
|
+
changed = true;
|
|
587
|
+
if (updated.previousVersion) {
|
|
588
|
+
warnings.push(`当前项目版本已从 ${updated.previousVersion} 切换到 ${updated.entry.version};旧版本默认改为 released。`);
|
|
589
|
+
}
|
|
590
|
+
if (updated.semver.warning) {
|
|
591
|
+
warnings.push(updated.semver.warning);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
if (options.status && !options.setVersion) {
|
|
596
|
+
const updated = setReleaseVersionStatus(ledger, options.status, { version: options.version });
|
|
597
|
+
ledger = updated.ledger;
|
|
598
|
+
actions.push('set-status');
|
|
599
|
+
changed = true;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
if (options.notes) {
|
|
603
|
+
const updated = appendReleaseEntry(ledger, options.notes, {
|
|
604
|
+
version: options.version,
|
|
605
|
+
fallbackType: '调整',
|
|
606
|
+
source: {
|
|
607
|
+
kind: 'manual-note',
|
|
608
|
+
manualId: `manual-note-${Date.now()}`,
|
|
609
|
+
},
|
|
610
|
+
});
|
|
611
|
+
ledger = updated.ledger;
|
|
612
|
+
actions.push('append-note');
|
|
613
|
+
changed = true;
|
|
614
|
+
}
|
|
615
|
+
} catch (error) {
|
|
616
|
+
return {
|
|
617
|
+
ok: false,
|
|
618
|
+
action: 'release',
|
|
619
|
+
projectRoot,
|
|
620
|
+
errors: [error instanceof Error ? error.message : String(error)],
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
if (changed) {
|
|
625
|
+
await saveReleaseLedger(projectRoot, ledger);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
const summary = buildReleaseLedgerSummary(ledger, { version: options.version });
|
|
629
|
+
const changeSummary = buildReleaseChangeSummary(ledger, {
|
|
630
|
+
version: options.version ?? summary.currentVersion,
|
|
631
|
+
limit: 5,
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
return {
|
|
635
|
+
ok: true,
|
|
636
|
+
action: 'release',
|
|
637
|
+
projectRoot,
|
|
638
|
+
releaseLedgerPath: loaded.filePath,
|
|
639
|
+
changed,
|
|
640
|
+
actions,
|
|
641
|
+
warnings,
|
|
642
|
+
summary,
|
|
643
|
+
changeSummary,
|
|
644
|
+
};
|
|
401
645
|
}
|
|
402
646
|
|
|
403
647
|
function reviewConfirmationCommand(snapshot) {
|
|
@@ -431,17 +675,9 @@ function assertPrdReviewConfirmedForChange(currentState, snapshot) {
|
|
|
431
675
|
async function generateOpenSpecChangeWorkspace(projectRoot, options = {}) {
|
|
432
676
|
const ws = await loadWorkspace(projectRoot);
|
|
433
677
|
const versionIndex = await readVersionIndex(ws);
|
|
434
|
-
const latest = await
|
|
678
|
+
const latest = await loadCurrentLaneSnapshot(ws, { fallbackToLatest: true });
|
|
435
679
|
const currentState = ws.data.currentState ?? {};
|
|
436
|
-
const snapshot = latest?.snapshot ??
|
|
437
|
-
...currentState,
|
|
438
|
-
versionNumber: currentState.prdVersion ?? (versionIndex.at(-1)?.versionNumber ?? 0),
|
|
439
|
-
versionId: currentState.prdVersion > 0
|
|
440
|
-
? formatVersionId(currentState.prdVersion)
|
|
441
|
-
: (versionIndex.at(-1)?.versionId ?? 'v0000'),
|
|
442
|
-
productType: resolveCurrentProductType(ws),
|
|
443
|
-
templatePack: resolveActiveTemplatePack(ws),
|
|
444
|
-
});
|
|
680
|
+
const snapshot = latest?.snapshot ?? buildCurrentStateSnapshot(ws, currentState, versionIndex);
|
|
445
681
|
assertPrdReviewConfirmedForChange(currentState, snapshot);
|
|
446
682
|
const analysis = analyzePrdSnapshot(snapshot);
|
|
447
683
|
const result = await writeOpenSpecChangeWorkspace(projectRoot, {
|
|
@@ -463,6 +699,7 @@ async function generateOpenSpecChangeWorkspace(projectRoot, options = {}) {
|
|
|
463
699
|
`验证: ${validation.valid ? '通过' : '失败'}。`,
|
|
464
700
|
]);
|
|
465
701
|
await syncSessionBindingFromChange(projectRoot, result.changeId, {
|
|
702
|
+
sessionId: ws.data.currentSessionId ?? null,
|
|
466
703
|
title: snapshot.title ?? null,
|
|
467
704
|
versionId: snapshot.versionId ?? null,
|
|
468
705
|
digest: snapshot.digest ?? null,
|
|
@@ -572,7 +809,7 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
572
809
|
}
|
|
573
810
|
|
|
574
811
|
if (command === 'doctor') {
|
|
575
|
-
const result = await doctorWorkspace(projectPath, { tools: flags.tools, hookProfile: flags.hookProfile, enableUserCodexConfig: true });
|
|
812
|
+
const result = await doctorWorkspace(projectPath, { tools: flags.tools, hookProfile: flags.hookProfile, fix: flags.fix, checkCodexRuntime: true, enableUserCodexConfig: true });
|
|
576
813
|
printDoctorResult(result, flags.json);
|
|
577
814
|
return result.ok ? 0 : 1;
|
|
578
815
|
}
|
|
@@ -604,6 +841,7 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
604
841
|
context: flags.context,
|
|
605
842
|
verify: flags.verify,
|
|
606
843
|
recordHook: flags.recordHook,
|
|
844
|
+
hookInject: flags.hookInject,
|
|
607
845
|
event: flags.event,
|
|
608
846
|
risk: flags.risk,
|
|
609
847
|
outcome: flags.outcome,
|
|
@@ -627,6 +865,7 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
627
865
|
message: flags.message,
|
|
628
866
|
evidence: flags.evidence,
|
|
629
867
|
notes: flags.notes,
|
|
868
|
+
repairAgent: flags.repairAgent,
|
|
630
869
|
};
|
|
631
870
|
if (flags.init) {
|
|
632
871
|
result = await initLoopWorkspace(projectPath, options);
|
|
@@ -674,10 +913,44 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
674
913
|
return result.ok ? 0 : 1;
|
|
675
914
|
}
|
|
676
915
|
|
|
916
|
+
if (command === 'knowledge') {
|
|
917
|
+
const subcommand = positionals[0] ?? 'candidates';
|
|
918
|
+
const firstArgIsProjectPath = !flags.path && positionals.length > 1 && await isDirectoryPath(positionals[1]);
|
|
919
|
+
const knowledgeProjectPath = path.resolve(flags.path ?? (firstArgIsProjectPath ? positionals[1] : process.cwd()));
|
|
920
|
+
let result;
|
|
921
|
+
if (subcommand === 'candidates' || subcommand === 'list') {
|
|
922
|
+
result = await listKnowledgeCandidates(knowledgeProjectPath, {
|
|
923
|
+
status: flags.status ?? 'pending-review',
|
|
924
|
+
});
|
|
925
|
+
} else if (subcommand === 'reject') {
|
|
926
|
+
result = await rejectKnowledgeCandidate(knowledgeProjectPath, {
|
|
927
|
+
id: flags.id ?? positionals[1] ?? null,
|
|
928
|
+
reason: flags.reason ?? flags.notes,
|
|
929
|
+
});
|
|
930
|
+
} else if (subcommand === 'archive') {
|
|
931
|
+
result = await archiveKnowledgeCandidate(knowledgeProjectPath, {
|
|
932
|
+
id: flags.id ?? positionals[1] ?? null,
|
|
933
|
+
reason: flags.reason ?? flags.notes,
|
|
934
|
+
});
|
|
935
|
+
} else if (subcommand === 'restore') {
|
|
936
|
+
result = await restoreKnowledgeCandidate(knowledgeProjectPath, {
|
|
937
|
+
id: flags.id ?? positionals[1] ?? null,
|
|
938
|
+
});
|
|
939
|
+
} else {
|
|
940
|
+
console.log('Usage: openprd knowledge <candidates|reject|archive|restore> [path-or-id] [--status <pending-review|all|rejected|archived|promoted|merged>] [--id <candidate-id>] [--reason <text>] [--json]');
|
|
941
|
+
return 1;
|
|
942
|
+
}
|
|
943
|
+
printKnowledgeResult(result, flags.json);
|
|
944
|
+
return result.ok ? 0 : 1;
|
|
945
|
+
}
|
|
946
|
+
|
|
677
947
|
if (command === 'visual-compare') {
|
|
678
948
|
const result = await visualCompareWorkspace(projectPath, {
|
|
679
949
|
reference: flags.reference,
|
|
680
950
|
actual: flags.actual,
|
|
951
|
+
before: flags.before,
|
|
952
|
+
after: flags.after,
|
|
953
|
+
board: flags.board,
|
|
681
954
|
out: flags.out,
|
|
682
955
|
format: flags.format,
|
|
683
956
|
quality: flags.quality,
|
|
@@ -733,6 +1006,14 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
733
1006
|
source: target ?? flags.source,
|
|
734
1007
|
notes: flags.notes,
|
|
735
1008
|
});
|
|
1009
|
+
} else if (subcommand === 'observe') {
|
|
1010
|
+
result = await observeBenchmarkSourceWorkspace(benchmarkProjectPath, {
|
|
1011
|
+
source: target ?? flags.source,
|
|
1012
|
+
notes: flags.notes,
|
|
1013
|
+
task: flags.item ?? flags.event,
|
|
1014
|
+
adoptedSignal: flags.status ?? flags.outcome,
|
|
1015
|
+
threshold: flags.threshold,
|
|
1016
|
+
});
|
|
736
1017
|
} else if (subcommand === 'approve') {
|
|
737
1018
|
result = await approveBenchmarkWorkspace(benchmarkProjectPath, {
|
|
738
1019
|
id: flags.id ?? target,
|
|
@@ -742,7 +1023,7 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
742
1023
|
} else if (subcommand === 'list') {
|
|
743
1024
|
result = await listBenchmarkWorkspace(benchmarkProjectPath);
|
|
744
1025
|
} else {
|
|
745
|
-
console.log('Usage: openprd benchmark <add|list|approve|verify> [target-or-id] [path-for-list-or-verify] [--path <project>] [--notes <text>] [--id <benchmark-id>]');
|
|
1026
|
+
console.log('Usage: openprd benchmark <add|observe|list|approve|verify> [target-or-id] [path-for-list-or-verify] [--path <project>] [--notes <text>] [--id <benchmark-id>]');
|
|
746
1027
|
return 1;
|
|
747
1028
|
}
|
|
748
1029
|
printBenchmarkResult(result, flags.json);
|
|
@@ -906,6 +1187,19 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
906
1187
|
return 0;
|
|
907
1188
|
}
|
|
908
1189
|
|
|
1190
|
+
if (command === 'release') {
|
|
1191
|
+
const result = await releaseWorkspace(projectPath, {
|
|
1192
|
+
enable: flags.enable,
|
|
1193
|
+
disable: flags.disable,
|
|
1194
|
+
setVersion: flags.set,
|
|
1195
|
+
status: flags.status,
|
|
1196
|
+
version: flags.version,
|
|
1197
|
+
notes: flags.notes,
|
|
1198
|
+
});
|
|
1199
|
+
printReleaseResult(result, flags.json);
|
|
1200
|
+
return result.ok ? 0 : 1;
|
|
1201
|
+
}
|
|
1202
|
+
|
|
909
1203
|
if (command === 'validate') {
|
|
910
1204
|
const { report } = await validateWorkspace(projectPath);
|
|
911
1205
|
printValidation(report, flags.json);
|
|
@@ -1012,7 +1306,9 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
1012
1306
|
evidence: flags.evidence,
|
|
1013
1307
|
notes: flags.notes,
|
|
1014
1308
|
};
|
|
1015
|
-
const result = flags.
|
|
1309
|
+
const result = flags.evidenceRequired
|
|
1310
|
+
? await checkOpenSpecTaskEvidenceWorkspace(projectPath, taskOptions)
|
|
1311
|
+
: flags.advance
|
|
1016
1312
|
? await advanceOpenSpecTaskWorkspace(projectPath, taskOptions)
|
|
1017
1313
|
: flags.verify
|
|
1018
1314
|
? await verifyOpenSpecTaskWorkspace(projectPath, taskOptions)
|
|
@@ -1094,6 +1390,7 @@ export {
|
|
|
1094
1390
|
nextWorkspace,
|
|
1095
1391
|
diffWorkspace,
|
|
1096
1392
|
historyWorkspace,
|
|
1393
|
+
releaseWorkspace,
|
|
1097
1394
|
reviewWorkspace,
|
|
1098
1395
|
reviewPresentationWorkspace,
|
|
1099
1396
|
freezeWorkspace,
|
|
@@ -1104,6 +1401,7 @@ export {
|
|
|
1104
1401
|
listOpenSpecTaskWorkspace,
|
|
1105
1402
|
advanceOpenSpecTaskWorkspace,
|
|
1106
1403
|
verifyOpenSpecTaskWorkspace,
|
|
1404
|
+
checkOpenSpecTaskEvidenceWorkspace,
|
|
1107
1405
|
listOpenPrdChangesWorkspace,
|
|
1108
1406
|
activateOpenPrdChangeWorkspace,
|
|
1109
1407
|
closeOpenPrdChangeWorkspace,
|
|
@@ -1121,6 +1419,10 @@ export {
|
|
|
1121
1419
|
verifyQualityWorkspace,
|
|
1122
1420
|
learnQualityWorkspace,
|
|
1123
1421
|
qualityWorkspace,
|
|
1422
|
+
listKnowledgeCandidates,
|
|
1423
|
+
rejectKnowledgeCandidate,
|
|
1424
|
+
archiveKnowledgeCandidate,
|
|
1425
|
+
restoreKnowledgeCandidate,
|
|
1124
1426
|
visualCompareWorkspace,
|
|
1125
1427
|
checkDevelopmentStandardsWorkspace,
|
|
1126
1428
|
initGrowthWorkspace,
|
|
@@ -1130,6 +1432,7 @@ export {
|
|
|
1130
1432
|
rejectGrowthCandidateWorkspace,
|
|
1131
1433
|
benchmarkWorkspace,
|
|
1132
1434
|
addBenchmarkWorkspace,
|
|
1435
|
+
observeBenchmarkSourceWorkspace,
|
|
1133
1436
|
listBenchmarkWorkspace,
|
|
1134
1437
|
approveBenchmarkWorkspace,
|
|
1135
1438
|
verifyBenchmarkWorkspace,
|
|
@@ -4,7 +4,6 @@ import YAML from 'yaml';
|
|
|
4
4
|
import { listChangeDirs, readDiscoveryConfig, resolveChangeDir } from './paths.js';
|
|
5
5
|
import { analyzeOpenSpecTaskVolumes } from './tasks.js';
|
|
6
6
|
import { checkStandardsWorkspace } from '../standards.js';
|
|
7
|
-
import { findOpenPrdSpecLanguageViolations } from '../language-policy.js';
|
|
8
7
|
|
|
9
8
|
function cjoin(...parts) {
|
|
10
9
|
return path.join(...parts);
|
|
@@ -70,7 +69,6 @@ const THEN_STEP_RE = /^-\s+\*\*(?:THEN|则|那么)\*\*/im;
|
|
|
70
69
|
function validateOpenSpecSpecText(relativePath, text, errors, checks) {
|
|
71
70
|
const requirementMatches = [...text.matchAll(REQUIREMENT_HEADING_RE)];
|
|
72
71
|
const sectionMatches = [...text.matchAll(SPEC_SECTION_HEADING_RE)];
|
|
73
|
-
const languageViolations = findOpenPrdSpecLanguageViolations(text);
|
|
74
72
|
|
|
75
73
|
if (sectionMatches.length === 0) {
|
|
76
74
|
errors.push(`${relativePath} 必须包含“新增需求”“修改需求”或“移除需求”章节。`);
|
|
@@ -104,13 +102,6 @@ function validateOpenSpecSpecText(relativePath, text, errors, checks) {
|
|
|
104
102
|
}
|
|
105
103
|
}
|
|
106
104
|
|
|
107
|
-
for (const violation of languageViolations.slice(0, 5)) {
|
|
108
|
-
errors.push(`${relativePath}:${violation.line} 不符合简体中文规则: ${violation.reason}。除必要专业字段、命令、文件名或英文产品名外,spec.md 正文必须使用简体中文。`);
|
|
109
|
-
}
|
|
110
|
-
if (languageViolations.length > 5) {
|
|
111
|
-
errors.push(`${relativePath} 还有 ${languageViolations.length - 5} 处简体中文规则问题。`);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
105
|
checks.push(`${relativePath}: ${requirementMatches.length} 个需求。`);
|
|
115
106
|
}
|
|
116
107
|
|