@openprd/cli 0.1.10 → 0.1.11
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/benchmarks/index.md +17 -15
- package/.openprd/benchmarks/sources.yaml +53 -1
- package/.openprd/schema/prd.schema.yaml +15 -0
- package/.openprd/templates/base/intake.md +13 -0
- package/.openprd/templates/base/prd.md +15 -0
- package/AGENTS.md +1 -1
- package/package.json +1 -1
- package/skills/openprd-benchmark-router/SKILL.md +2 -1
- package/skills/openprd-requirement-intake/SKILL.md +3 -3
- package/skills/openprd-requirement-intake/references/prd-template-lenses.md +2 -2
- package/skills/openprd-requirement-intake/references/startup-validation-lens.md +98 -13
- package/skills/openprd-shared/SKILL.md +1 -1
- package/src/agent-integration.js +6 -6
- package/src/brainstorm-artifacts.js +156 -8
- package/src/brainstorm-presentation.js +14 -4
- package/src/brainstorm.js +165 -2
- package/src/cli/quality-print.js +9 -3
- package/src/codex-hook-runner-template.mjs +6 -6
- package/src/diagram-core.js +384 -151
- package/src/html-artifacts.js +403 -50
- package/src/knowledge.js +59 -12
- package/src/prd-core.js +101 -2
- package/src/quality-html-artifact.js +30 -5
- package/src/quality.js +20 -10
- package/src/workspace-core.js +38 -0
- package/src/workspace-workflow.js +126 -9
|
@@ -24,6 +24,7 @@ const BRAINSTORM_PANEL_ORDER = [
|
|
|
24
24
|
'marketSignals',
|
|
25
25
|
'validationLoop',
|
|
26
26
|
'businessViability',
|
|
27
|
+
'minimalistReview',
|
|
27
28
|
'risks',
|
|
28
29
|
'reuseOpportunities',
|
|
29
30
|
];
|
|
@@ -46,13 +47,18 @@ const BRAINSTORM_PANEL_META = {
|
|
|
46
47
|
},
|
|
47
48
|
validationLoop: {
|
|
48
49
|
title: '验证闭环',
|
|
49
|
-
description: '
|
|
50
|
-
emptyText: '
|
|
50
|
+
description: '先说清去哪里找人、为什么你能先触达、用户现在怎么解决,以及不做完整产品时先怎么跑起来。',
|
|
51
|
+
emptyText: '待补充社区入口、社区契合、当前替代方案和手工交付路径。',
|
|
51
52
|
},
|
|
52
53
|
businessViability: {
|
|
53
54
|
title: '商业闭环',
|
|
54
|
-
description: '
|
|
55
|
-
emptyText: '
|
|
55
|
+
description: '先定义真实承诺、付费验证、最低成本验证动作,以及验证阶段怎样先活下来。',
|
|
56
|
+
emptyText: '待补充承诺信号、付费证明、最低成本验证和先活下来方案。',
|
|
57
|
+
},
|
|
58
|
+
minimalistReview: {
|
|
59
|
+
title: '极简判断',
|
|
60
|
+
description: '把保持小的执行方式、产品化门槛、可逆性和价值观判断摆出来,避免自嗨式扩张。',
|
|
61
|
+
emptyText: '待补充保持小的做法、产品化门槛和极简判断。',
|
|
56
62
|
},
|
|
57
63
|
reuseOpportunities: {
|
|
58
64
|
title: '现有基础与复用',
|
|
@@ -77,6 +83,7 @@ const BRAINSTORM_TONE_META = {
|
|
|
77
83
|
marketSignals: { tone: 'map', icon: 'market' },
|
|
78
84
|
validationLoop: { tone: 'success', icon: 'goal' },
|
|
79
85
|
businessViability: { tone: 'guardrail', icon: 'next' },
|
|
86
|
+
minimalistReview: { tone: 'map', icon: 'risk' },
|
|
80
87
|
reuseOpportunities: { tone: 'guardrail', icon: 'reuse' },
|
|
81
88
|
risks: { tone: 'risk', icon: 'risk' },
|
|
82
89
|
nextSteps: { tone: 'success', icon: 'next' },
|
|
@@ -119,6 +126,7 @@ const BRAINSTORM_PRESENTATION_CONTRACT = {
|
|
|
119
126
|
marketSignals: [{ summary: '15 字以内标签', detail: '60 字以内说明' }],
|
|
120
127
|
validationLoop: [{ summary: '15 字以内标签', detail: '60 字以内说明' }],
|
|
121
128
|
businessViability: [{ summary: '15 字以内标签', detail: '60 字以内说明' }],
|
|
129
|
+
minimalistReview: [{ summary: '15 字以内标签', detail: '60 字以内说明' }],
|
|
122
130
|
reuseOpportunities: [{ summary: '15 字以内标签', detail: '60 字以内说明' }],
|
|
123
131
|
risks: [{ summary: '15 字以内标签', detail: '60 字以内说明' }],
|
|
124
132
|
},
|
|
@@ -608,11 +616,26 @@ function buildBrainstormCapturePatch(record) {
|
|
|
608
616
|
'users.stakeholders': { value: listOfStrings(captureState.stakeholders), source: 'user-confirmed' },
|
|
609
617
|
'validation.community': { value: listOfStrings(captureState.community), source: 'user-confirmed' },
|
|
610
618
|
'validation.seedUsers': { value: listOfStrings(captureState.seedUsers), source: 'user-confirmed' },
|
|
619
|
+
'validation.communityFit': { value: listOfStrings(captureState.communityFit), source: 'user-confirmed' },
|
|
611
620
|
'validation.currentAlternative': { value: normalizedText(captureState.currentAlternative || captureState.asIs), source: 'user-confirmed' },
|
|
621
|
+
'validation.painEvidence': { value: listOfStrings(captureState.painEvidence), source: 'user-confirmed' },
|
|
612
622
|
'validation.manualPath': { value: listOfStrings(captureState.manualPath), source: 'user-confirmed' },
|
|
623
|
+
'validation.manualPlaybook': { value: listOfStrings(captureState.manualPlaybook), source: 'user-confirmed' },
|
|
613
624
|
'validation.commitmentSignals': { value: listOfStrings(captureState.commitmentSignals), source: 'user-confirmed' },
|
|
614
625
|
'validation.firstValidationStep': { value: normalizedText(captureState.firstValidationStep || captureState.nextStep), source: 'user-confirmed' },
|
|
615
626
|
'validation.defaultAlivePlan': { value: listOfStrings(captureState.defaultAlivePlan), source: 'user-confirmed' },
|
|
627
|
+
'validation.paymentProof': { value: listOfStrings(captureState.paymentProof), source: 'user-confirmed' },
|
|
628
|
+
'validation.mvpSlice': { value: normalizedText(captureState.mvpSlice), source: 'user-confirmed' },
|
|
629
|
+
'validation.weekendTest': { value: normalizedText(captureState.weekendTest), source: 'user-confirmed' },
|
|
630
|
+
'validation.smallestExecution': { value: listOfStrings(captureState.smallestExecution), source: 'user-confirmed' },
|
|
631
|
+
'validation.productizeGate': { value: listOfStrings(captureState.productizeGate), source: 'user-confirmed' },
|
|
632
|
+
'validation.firstCustomerPath': { value: listOfStrings(captureState.firstCustomerPath), source: 'user-confirmed' },
|
|
633
|
+
'validation.pricingHypothesis': { value: normalizedText(captureState.pricingHypothesis), source: 'user-confirmed' },
|
|
634
|
+
'validation.customerOneProfitability': { value: normalizedText(captureState.customerOneProfitability), source: 'user-confirmed' },
|
|
635
|
+
'validation.growthDiscipline': { value: listOfStrings(captureState.growthDiscipline), source: 'user-confirmed' },
|
|
636
|
+
'validation.reversibility': { value: normalizedText(captureState.reversibility), source: 'user-confirmed' },
|
|
637
|
+
'validation.customerTruth': { value: normalizedText(captureState.customerTruth), source: 'user-confirmed' },
|
|
638
|
+
'validation.valuesFit': { value: normalizedText(captureState.valuesFit), source: 'user-confirmed' },
|
|
616
639
|
'goals.goals': { value: listOfStrings(captureState.goals), source: 'user-confirmed' },
|
|
617
640
|
'goals.successMetrics': { value: listOfStrings(captureState.successMetrics), source: 'user-confirmed' },
|
|
618
641
|
'scope.inScope': { value: listOfStrings(captureState.inScope), source: 'user-confirmed' },
|
|
@@ -657,16 +680,31 @@ export function renderBrainstormMarkdown({ record }) {
|
|
|
657
680
|
]],
|
|
658
681
|
['当前替代方案', [
|
|
659
682
|
summarizeText(normalizedText(record?.captureState?.currentAlternative || record?.captureState?.asIs), '待补充现在主要怎么解决'),
|
|
683
|
+
...listOfStrings(record?.captureState?.communityFit).map((item) => `社区契合:${item}`),
|
|
684
|
+
...listOfStrings(record?.captureState?.painEvidence).map((item) => `痛点证据:${item}`),
|
|
660
685
|
summarizeText(record?.captureState?.whyNow, '待补充为什么现在值得做'),
|
|
661
686
|
]],
|
|
662
687
|
['验证闭环', [
|
|
663
688
|
...listOfStrings(record?.report?.validationLoop),
|
|
664
689
|
...listOfStrings(record?.captureState?.manualPath).map((item) => `手工路径:${item}`),
|
|
690
|
+
...listOfStrings(record?.captureState?.manualPlaybook).map((item) => `手工作战卡:${item}`),
|
|
665
691
|
]],
|
|
666
692
|
['商业闭环', [
|
|
667
693
|
...listOfStrings(record?.report?.businessViability),
|
|
668
694
|
...listOfStrings(record?.captureState?.commitmentSignals).map((item) => `承诺信号:${item}`),
|
|
695
|
+
...listOfStrings(record?.captureState?.paymentProof).map((item) => `付费证明:${item}`),
|
|
696
|
+
...listOfStrings(record?.captureState?.growthDiscipline).map((item) => `增长纪律:${item}`),
|
|
697
|
+
]],
|
|
698
|
+
['一件事 MVP 与成交路径', [
|
|
699
|
+
summarizeText(record?.captureState?.mvpSlice, '待补充第一版只做一件事'),
|
|
700
|
+
summarizeText(record?.captureState?.weekendTest, '待补充周末级验证'),
|
|
701
|
+
...listOfStrings(record?.captureState?.smallestExecution).map((item) => `最小工具桥接:${item}`),
|
|
702
|
+
...listOfStrings(record?.captureState?.productizeGate).map((item) => `产品化门槛:${item}`),
|
|
703
|
+
...listOfStrings(record?.captureState?.firstCustomerPath).map((item) => `第一批客户路径:${item}`),
|
|
704
|
+
summarizeText(record?.captureState?.pricingHypothesis, '待补充初始收费假设'),
|
|
705
|
+
summarizeText(record?.captureState?.customerOneProfitability, '待补充客户 1 盈利路径'),
|
|
669
706
|
]],
|
|
707
|
+
['极简判断', listOfStrings(record?.report?.minimalistReview)],
|
|
670
708
|
['推荐方向', [
|
|
671
709
|
summarizeText(record?.summary?.recommendedDirection, '待补充推荐方向'),
|
|
672
710
|
...listOfStrings(record.report?.directionOptions).slice(0, 3),
|
|
@@ -730,13 +768,14 @@ function renderPanel(kind, items) {
|
|
|
730
768
|
const meta = BRAINSTORM_PANEL_META[kind];
|
|
731
769
|
const normalized = items.filter((item) => item.summary || item.detail);
|
|
732
770
|
return `
|
|
733
|
-
<section class="brainstorm-panel brainstorm-panel-${escapeHtml(brainstormTone(kind).tone)}">
|
|
771
|
+
<section class="brainstorm-panel brainstorm-panel-${escapeHtml(brainstormTone(kind).tone)}" id="brainstorm-panel-${escapeHtml(kind)}">
|
|
734
772
|
<div class="panel-head">
|
|
735
773
|
${brainstormIcon(kind)}
|
|
736
774
|
<div>
|
|
737
775
|
<h3>${escapeHtml(meta.title)}</h3>
|
|
738
776
|
<p>${escapeHtml(meta.description)}</p>
|
|
739
777
|
</div>
|
|
778
|
+
<span class="panel-count${normalized.length === 0 ? ' empty' : ''}">${normalized.length === 0 ? '还没聊到' : `${normalized.length} 条`}</span>
|
|
740
779
|
</div>
|
|
741
780
|
<div class="chip-row">
|
|
742
781
|
${(normalized.length > 0
|
|
@@ -989,8 +1028,18 @@ function buildBrainstormExportPayload(record, artifactPaths) {
|
|
|
989
1028
|
currentAlternative: record.summary?.currentAlternative ?? null,
|
|
990
1029
|
confidenceLabel: record.summary?.confidenceLabel ?? null,
|
|
991
1030
|
assumptions: listOfStrings(record.captureState?.assumptions).slice(0, 3),
|
|
1031
|
+
communityFit: listOfStrings(record.captureState?.communityFit).slice(0, 3),
|
|
1032
|
+
painEvidence: listOfStrings(record.captureState?.painEvidence).slice(0, 3),
|
|
992
1033
|
commitmentSignals: listOfStrings(record.captureState?.commitmentSignals).slice(0, 3),
|
|
1034
|
+
paymentProof: listOfStrings(record.captureState?.paymentProof).slice(0, 3),
|
|
993
1035
|
defaultAlivePlan: listOfStrings(record.captureState?.defaultAlivePlan).slice(0, 3),
|
|
1036
|
+
manualPlaybook: listOfStrings(record.captureState?.manualPlaybook).slice(0, 3),
|
|
1037
|
+
smallestExecution: listOfStrings(record.captureState?.smallestExecution).slice(0, 3),
|
|
1038
|
+
productizeGate: listOfStrings(record.captureState?.productizeGate).slice(0, 3),
|
|
1039
|
+
growthDiscipline: listOfStrings(record.captureState?.growthDiscipline).slice(0, 3),
|
|
1040
|
+
firstCustomerPath: listOfStrings(record.captureState?.firstCustomerPath).slice(0, 3),
|
|
1041
|
+
mvpSlice: normalizedText(record.captureState?.mvpSlice) || null,
|
|
1042
|
+
pricingHypothesis: normalizedText(record.captureState?.pricingHypothesis) || null,
|
|
994
1043
|
stopLossActions: listOfStrings(record.captureState?.stopLossActions).slice(0, 3),
|
|
995
1044
|
openQuestions: listOfStrings(record.report?.openQuestions).slice(0, 5),
|
|
996
1045
|
markdownPath: artifactPaths.markdownPath,
|
|
@@ -1055,10 +1104,27 @@ export function renderBrainstormArtifact({ record, markdownPath, patchPath, stat
|
|
|
1055
1104
|
`谁先要用:${joinedText(record?.captureState?.primaryUsers, { fallback: '待补充' })}`,
|
|
1056
1105
|
`整理时间:${record.generatedAt}`,
|
|
1057
1106
|
];
|
|
1058
|
-
const
|
|
1107
|
+
const panelEntries = BRAINSTORM_PANEL_ORDER.map((kind) => {
|
|
1059
1108
|
const fallbackItems = listOfStrings(record.report?.[kind]);
|
|
1060
|
-
|
|
1061
|
-
|
|
1109
|
+
const items = presentationPanelItems(record, kind, fallbackItems);
|
|
1110
|
+
const count = items.filter((item) => item.summary || item.detail).length;
|
|
1111
|
+
return { kind, items, count, title: BRAINSTORM_PANEL_META[kind].title };
|
|
1112
|
+
});
|
|
1113
|
+
const panels = panelEntries.map((entry) => renderPanel(entry.kind, entry.items)).join('');
|
|
1114
|
+
const emptyPanels = panelEntries.filter((entry) => entry.count === 0);
|
|
1115
|
+
const agendaMarkup = `
|
|
1116
|
+
<nav class="brainstorm-agenda" aria-label="讨论清单">
|
|
1117
|
+
<p class="brainstorm-agenda-title">这次讨论共 ${panelEntries.length} 块${emptyPanels.length > 0 ? `,还有 ${emptyPanels.length} 块没聊到` : ',每一块都有内容了'}</p>
|
|
1118
|
+
<div class="brainstorm-agenda-links">
|
|
1119
|
+
${panelEntries.map((entry, index) => `
|
|
1120
|
+
<a href="#brainstorm-panel-${escapeHtml(entry.kind)}" class="brainstorm-agenda-link${entry.count === 0 ? ' empty' : ''}">
|
|
1121
|
+
<span class="brainstorm-agenda-index">${index + 1}</span>
|
|
1122
|
+
<span>${escapeHtml(entry.title)}</span>
|
|
1123
|
+
<span class="brainstorm-agenda-count">${entry.count === 0 ? '待聊' : `${entry.count} 条`}</span>
|
|
1124
|
+
</a>`).join('')}
|
|
1125
|
+
</div>
|
|
1126
|
+
</nav>
|
|
1127
|
+
`;
|
|
1062
1128
|
const artifactPaths = { markdownPath, patchPath, statePath };
|
|
1063
1129
|
|
|
1064
1130
|
return `<!DOCTYPE html>
|
|
@@ -1381,6 +1447,87 @@ export function renderBrainstormArtifact({ record, markdownPath, patchPath, stat
|
|
|
1381
1447
|
gap: 12px;
|
|
1382
1448
|
align-items: flex-start;
|
|
1383
1449
|
}
|
|
1450
|
+
.panel-count {
|
|
1451
|
+
flex-shrink: 0;
|
|
1452
|
+
margin-left: auto;
|
|
1453
|
+
padding: 3px 10px;
|
|
1454
|
+
border-radius: 999px;
|
|
1455
|
+
border: 1px solid rgba(37,99,235,0.25);
|
|
1456
|
+
background: rgba(37,99,235,0.06);
|
|
1457
|
+
color: var(--review-blue);
|
|
1458
|
+
font-size: 12px;
|
|
1459
|
+
font-weight: 600;
|
|
1460
|
+
white-space: nowrap;
|
|
1461
|
+
}
|
|
1462
|
+
.panel-count.empty {
|
|
1463
|
+
border-color: var(--review-line);
|
|
1464
|
+
background: var(--review-panel-soft);
|
|
1465
|
+
color: var(--review-muted);
|
|
1466
|
+
font-weight: 400;
|
|
1467
|
+
}
|
|
1468
|
+
.brainstorm-agenda {
|
|
1469
|
+
margin-top: 18px;
|
|
1470
|
+
padding: 16px 20px;
|
|
1471
|
+
border-radius: 16px;
|
|
1472
|
+
border: 1px solid var(--review-line);
|
|
1473
|
+
background: var(--review-panel);
|
|
1474
|
+
box-shadow: 0 10px 24px rgba(23,32,51,0.05);
|
|
1475
|
+
}
|
|
1476
|
+
.brainstorm-agenda-title {
|
|
1477
|
+
margin: 0;
|
|
1478
|
+
color: var(--review-text);
|
|
1479
|
+
font-size: 14px;
|
|
1480
|
+
font-weight: 700;
|
|
1481
|
+
}
|
|
1482
|
+
.brainstorm-agenda-links {
|
|
1483
|
+
margin-top: 12px;
|
|
1484
|
+
display: flex;
|
|
1485
|
+
flex-wrap: wrap;
|
|
1486
|
+
gap: 8px;
|
|
1487
|
+
}
|
|
1488
|
+
.brainstorm-agenda-link {
|
|
1489
|
+
display: inline-flex;
|
|
1490
|
+
align-items: center;
|
|
1491
|
+
gap: 8px;
|
|
1492
|
+
padding: 6px 12px;
|
|
1493
|
+
border-radius: 999px;
|
|
1494
|
+
border: 1px solid var(--review-line);
|
|
1495
|
+
background: var(--review-panel-soft);
|
|
1496
|
+
color: var(--review-text);
|
|
1497
|
+
text-decoration: none;
|
|
1498
|
+
font-size: 13px;
|
|
1499
|
+
line-height: 1.5;
|
|
1500
|
+
}
|
|
1501
|
+
.brainstorm-agenda-link:hover {
|
|
1502
|
+
border-color: rgba(37,99,235,0.4);
|
|
1503
|
+
background: #ffffff;
|
|
1504
|
+
}
|
|
1505
|
+
.brainstorm-agenda-link.empty { color: var(--review-muted); }
|
|
1506
|
+
.brainstorm-agenda-index {
|
|
1507
|
+
display: inline-flex;
|
|
1508
|
+
align-items: center;
|
|
1509
|
+
justify-content: center;
|
|
1510
|
+
width: 18px;
|
|
1511
|
+
height: 18px;
|
|
1512
|
+
border-radius: 999px;
|
|
1513
|
+
background: rgba(37,99,235,0.1);
|
|
1514
|
+
color: var(--review-blue);
|
|
1515
|
+
font-size: 11px;
|
|
1516
|
+
font-weight: 700;
|
|
1517
|
+
}
|
|
1518
|
+
.brainstorm-agenda-link.empty .brainstorm-agenda-index {
|
|
1519
|
+
background: var(--review-line);
|
|
1520
|
+
color: var(--review-muted);
|
|
1521
|
+
}
|
|
1522
|
+
.brainstorm-agenda-count {
|
|
1523
|
+
color: var(--review-blue);
|
|
1524
|
+
font-size: 12px;
|
|
1525
|
+
font-weight: 600;
|
|
1526
|
+
}
|
|
1527
|
+
.brainstorm-agenda-link.empty .brainstorm-agenda-count {
|
|
1528
|
+
color: var(--review-muted);
|
|
1529
|
+
font-weight: 400;
|
|
1530
|
+
}
|
|
1384
1531
|
.brainstorm-icon {
|
|
1385
1532
|
flex: 0 0 auto;
|
|
1386
1533
|
display: inline-flex;
|
|
@@ -1605,6 +1752,7 @@ export function renderBrainstormArtifact({ record, markdownPath, patchPath, stat
|
|
|
1605
1752
|
</section>
|
|
1606
1753
|
</section>
|
|
1607
1754
|
|
|
1755
|
+
${agendaMarkup}
|
|
1608
1756
|
${visualScenes.map((scene, index) => renderVisualScene(scene, index)).join('\n')}
|
|
1609
1757
|
<section class="panel-grid">${panels}</section>
|
|
1610
1758
|
</main>
|
|
@@ -68,13 +68,23 @@ export const BRAINSTORM_PRESENTATION_TEMPLATE = {
|
|
|
68
68
|
],
|
|
69
69
|
validationLoop: [
|
|
70
70
|
{ summary: '先找谁验', detail: '先补第一批最容易触达、最可能给真实反馈的人。' },
|
|
71
|
-
{ summary: '
|
|
72
|
-
{ summary: '
|
|
71
|
+
{ summary: '为什么是你', detail: '先说清你为什么算这个社区里的自己人、为什么现在就能触达。' },
|
|
72
|
+
{ summary: '当前替代', detail: '先说清用户现在主要靠什么办法在解决,以及这个办法到底多痛。' },
|
|
73
|
+
{ summary: '手工作战卡', detail: '把触发条件、步骤、工具、耗时和交接点先写清楚。' },
|
|
73
74
|
],
|
|
74
75
|
businessViability: [
|
|
75
76
|
{ summary: '承诺信号', detail: '先定义什么真实承诺最能证明值得继续。' },
|
|
77
|
+
{ summary: '付费证明', detail: '先补有没有 10 个样本、3/10 愿意付费或更强交易信号。' },
|
|
76
78
|
{ summary: '最低成本验证', detail: '优先写最便宜的验证动作,不要一上来做全套方案。' },
|
|
77
|
-
{ summary: '
|
|
79
|
+
{ summary: '先怎么收费', detail: '先给一个从第一个客户开始就成立的收费假设。' },
|
|
80
|
+
{ summary: '增长纪律', detail: '先约束什么时候不该 launch、什么时候别急着花钱或扩团队。' },
|
|
81
|
+
],
|
|
82
|
+
minimalistReview: [
|
|
83
|
+
{ summary: '更小的执行', detail: '先判断能不能继续用 spreadsheet、表单或 no-code 保持小;如果必须开始做产品,也只自动化最重复的一步并先压成 forms / lists / CRUD 骨架,不要先为假想中的未来客户造复杂能力。' },
|
|
84
|
+
{ summary: '产品化门槛', detail: '先写清达到什么条件才允许继续产品化或加功能。' },
|
|
85
|
+
{ summary: '是否可逆', detail: '先判断验证结果一般时,这条路是不是容易回退,避免先做重招聘、长期绑定或重平台化。' },
|
|
86
|
+
{ summary: '客户真问题', detail: '先看这更像客户痛点,还是团队自己的技术冲动。' },
|
|
87
|
+
{ summary: '价值观一致', detail: '先确认这条路是否符合团队想坚持的做法,以及它是不是你愿意长期住进去的业务形态。' },
|
|
78
88
|
],
|
|
79
89
|
reuseOpportunities: [
|
|
80
90
|
{ summary: '现有基础', detail: '指出现在已经有什么能直接借,不要默认从零开始。' },
|
|
@@ -90,7 +100,7 @@ export const BRAINSTORM_PRESENTATION_TEMPLATE = {
|
|
|
90
100
|
|
|
91
101
|
export function buildBrainstormPresentationTemplatePayload() {
|
|
92
102
|
return {
|
|
93
|
-
intent: 'Agent 先按这个模板写 brainstormPresentation
|
|
103
|
+
intent: 'Agent 先按这个模板写 brainstormPresentation,再用本脚本校验;页面重点不只是整理需求,还要把当前替代方案、社区契合、痛点证据、手工作战卡、一件事 MVP、最小工具桥接、首批成交、产品化门槛、增长纪律、关键前提、验证动作和止损线说清楚。除了正文卡片,还可以用 visualScenes 生成更灵活的可视化区域,但仍然要走统一契约,不直接手写任意 SVG。',
|
|
94
104
|
presentationTemplate: BRAINSTORM_PRESENTATION_TEMPLATE,
|
|
95
105
|
presentationContract: BRAINSTORM_PRESENTATION_CONTRACT,
|
|
96
106
|
};
|
package/src/brainstorm.js
CHANGED
|
@@ -180,16 +180,32 @@ function buildConfidenceLabel(currentState, benchmark, knowledge) {
|
|
|
180
180
|
normalizedText(currentState.problemStatement),
|
|
181
181
|
listOfStrings(currentState.primaryUsers).length > 0 ? 'users' : null,
|
|
182
182
|
listOfStrings(currentState.community).length > 0 ? 'community' : null,
|
|
183
|
+
listOfStrings(currentState.communityFit).length > 0 ? 'community-fit' : null,
|
|
183
184
|
normalizedText(currentState.currentAlternative || currentState.asIs) ? 'current-alternative' : null,
|
|
185
|
+
listOfStrings(currentState.painEvidence).length > 0 ? 'pain-evidence' : null,
|
|
186
|
+
listOfStrings(currentState.manualPath).length > 0 ? 'manual-path' : null,
|
|
187
|
+
listOfStrings(currentState.manualPlaybook).length > 0 ? 'manual-playbook' : null,
|
|
184
188
|
listOfStrings(currentState.commitmentSignals).length > 0 ? 'commitment' : null,
|
|
185
189
|
normalizedText(currentState.firstValidationStep || currentState.nextStep) ? 'validation-step' : null,
|
|
190
|
+
listOfStrings(currentState.paymentProof).length > 0 ? 'payment-proof' : null,
|
|
191
|
+
normalizedText(currentState.mvpSlice) ? 'mvp-slice' : null,
|
|
192
|
+
normalizedText(currentState.weekendTest) ? 'weekend-test' : null,
|
|
193
|
+
listOfStrings(currentState.smallestExecution).length > 0 ? 'smallest-execution' : null,
|
|
194
|
+
listOfStrings(currentState.productizeGate).length > 0 ? 'productize-gate' : null,
|
|
195
|
+
listOfStrings(currentState.firstCustomerPath).length > 0 ? 'first-customer-path' : null,
|
|
196
|
+
normalizedText(currentState.pricingHypothesis) ? 'pricing' : null,
|
|
197
|
+
normalizedText(currentState.customerOneProfitability) ? 'customer-one-profitability' : null,
|
|
198
|
+
listOfStrings(currentState.growthDiscipline).length > 0 ? 'growth-discipline' : null,
|
|
199
|
+
normalizedText(currentState.reversibility) ? 'reversibility' : null,
|
|
200
|
+
normalizedText(currentState.customerTruth) ? 'customer-truth' : null,
|
|
201
|
+
normalizedText(currentState.valuesFit) ? 'values-fit' : null,
|
|
186
202
|
listOfStrings(currentState.goals).length > 0 ? 'goals' : null,
|
|
187
203
|
listOfStrings(currentState.assumptions).length > 0 ? 'assumptions' : null,
|
|
188
204
|
benchmark.counts.approved > 0 ? 'benchmark' : null,
|
|
189
205
|
knowledge.counts.skills > 0 ? 'knowledge' : null,
|
|
190
206
|
].filter(Boolean).length;
|
|
191
|
-
if (signals >=
|
|
192
|
-
if (signals >=
|
|
207
|
+
if (signals >= 8) return '已经有较完整基础,可以开始整理 PRD';
|
|
208
|
+
if (signals >= 5) return '方向基本有了,但还差 1 轮确认';
|
|
193
209
|
return '现在信息还不够,先继续把问题聊透';
|
|
194
210
|
}
|
|
195
211
|
|
|
@@ -207,6 +223,15 @@ function buildFollowUpPrompts(currentState, workspaceScan) {
|
|
|
207
223
|
if (listOfStrings(currentState.manualPath).length === 0) {
|
|
208
224
|
prompts.push('先补一条不做完整产品时也能手工交付价值的路径。');
|
|
209
225
|
}
|
|
226
|
+
if (listOfStrings(currentState.manualPlaybook).length === 0) {
|
|
227
|
+
prompts.push('先把手工作战卡写出来,至少补齐触发条件、步骤、工具、耗时和交接点。');
|
|
228
|
+
}
|
|
229
|
+
if (listOfStrings(currentState.communityFit).length === 0) {
|
|
230
|
+
prompts.push('先说清你为什么算这个社区里的自己人、他们在哪里聚集、为什么这个切口足够小,以及你现在是否已经在真实参与或贡献。');
|
|
231
|
+
}
|
|
232
|
+
if (listOfStrings(currentState.painEvidence).length === 0) {
|
|
233
|
+
prompts.push('先证明这个问题真的痛,用户现在已经在为更差的办法花时间或花钱。');
|
|
234
|
+
}
|
|
210
235
|
if (listOfStrings(currentState.commitmentSignals).length === 0) {
|
|
211
236
|
prompts.push('先定义什么真实承诺能证明这不是口头兴趣。');
|
|
212
237
|
}
|
|
@@ -216,6 +241,42 @@ function buildFollowUpPrompts(currentState, workspaceScan) {
|
|
|
216
241
|
if (listOfStrings(currentState.defaultAlivePlan).length === 0) {
|
|
217
242
|
prompts.push('先说清验证阶段怎样控制成本、时间和交付方式,确保这件事先活下来。');
|
|
218
243
|
}
|
|
244
|
+
if (listOfStrings(currentState.paymentProof).length === 0) {
|
|
245
|
+
prompts.push('先补有没有 10 个样本、3/10 付费意愿或更强交易信号。');
|
|
246
|
+
}
|
|
247
|
+
if (!normalizedText(currentState.mvpSlice)) {
|
|
248
|
+
prompts.push('先收敛第一版到底只做哪一件事。');
|
|
249
|
+
}
|
|
250
|
+
if (!normalizedText(currentState.weekendTest)) {
|
|
251
|
+
prompts.push('先判断这件事能不能压成周末级 MVP 或更轻试跑。');
|
|
252
|
+
}
|
|
253
|
+
if (listOfStrings(currentState.smallestExecution).length === 0) {
|
|
254
|
+
prompts.push('先说清能不能用 spreadsheet、表单或 no-code 工具把第一版跑起来;如果必须开始做产品,也只自动化最重复的一步,先压成 forms / lists / CRUD 骨架,不要先为假想中的未来客户造复杂能力。');
|
|
255
|
+
}
|
|
256
|
+
if (listOfStrings(currentState.productizeGate).length === 0) {
|
|
257
|
+
prompts.push('先写清达到什么条件才允许继续产品化或加功能,先服务今天已经存在的重复需求,不要提前为未来规模开壳。');
|
|
258
|
+
}
|
|
259
|
+
if (listOfStrings(currentState.firstCustomerPath).length === 0) {
|
|
260
|
+
prompts.push('先说清第一批客户最现实的触达顺序。');
|
|
261
|
+
}
|
|
262
|
+
if (!normalizedText(currentState.pricingHypothesis)) {
|
|
263
|
+
prompts.push('先给出一个从第一个客户开始就成立的收费假设。');
|
|
264
|
+
}
|
|
265
|
+
if (!normalizedText(currentState.customerOneProfitability)) {
|
|
266
|
+
prompts.push('先判断客户 1 怎么覆盖时间和交付成本。');
|
|
267
|
+
}
|
|
268
|
+
if (listOfStrings(currentState.growthDiscipline).length === 0) {
|
|
269
|
+
prompts.push('先约束增长纪律:先熟人还是先社区、什么时候不该 launch、什么时候别急着花钱。');
|
|
270
|
+
}
|
|
271
|
+
if (!normalizedText(currentState.reversibility)) {
|
|
272
|
+
prompts.push('先说清如果验证结果一般,这条路有多容易回退,会不会逼你做重招聘、长期绑定或重平台化这类不可逆决策。');
|
|
273
|
+
}
|
|
274
|
+
if (!normalizedText(currentState.customerTruth)) {
|
|
275
|
+
prompts.push('先判断这更像客户真问题,还是团队自己的技术冲动。');
|
|
276
|
+
}
|
|
277
|
+
if (!normalizedText(currentState.valuesFit)) {
|
|
278
|
+
prompts.push('先确认这条路是否符合团队现在想坚持的价值观和长期经营方式;如果连续这样做 3 到 5 年,你还愿意住在这套业务里吗?');
|
|
279
|
+
}
|
|
219
280
|
if (listOfStrings(currentState.goals).length === 0) {
|
|
220
281
|
prompts.push('先把想达到的目标和判断标准说清楚。');
|
|
221
282
|
}
|
|
@@ -260,10 +321,25 @@ function buildBrainstormReport({ currentState, benchmark, knowledge, workspaceSc
|
|
|
260
321
|
const community = listOfStrings(currentState.community);
|
|
261
322
|
const seedUsers = listOfStrings(currentState.seedUsers);
|
|
262
323
|
const currentAlternative = normalizedText(currentState.currentAlternative || currentState.asIs);
|
|
324
|
+
const communityFit = listOfStrings(currentState.communityFit);
|
|
325
|
+
const painEvidence = listOfStrings(currentState.painEvidence);
|
|
263
326
|
const manualPath = listOfStrings(currentState.manualPath);
|
|
327
|
+
const manualPlaybook = listOfStrings(currentState.manualPlaybook);
|
|
264
328
|
const commitmentSignals = listOfStrings(currentState.commitmentSignals);
|
|
265
329
|
const firstValidationStep = normalizedText(currentState.firstValidationStep || currentState.nextStep);
|
|
266
330
|
const defaultAlivePlan = listOfStrings(currentState.defaultAlivePlan);
|
|
331
|
+
const paymentProof = listOfStrings(currentState.paymentProof);
|
|
332
|
+
const mvpSlice = normalizedText(currentState.mvpSlice);
|
|
333
|
+
const weekendTest = normalizedText(currentState.weekendTest);
|
|
334
|
+
const smallestExecution = listOfStrings(currentState.smallestExecution);
|
|
335
|
+
const productizeGate = listOfStrings(currentState.productizeGate);
|
|
336
|
+
const firstCustomerPath = listOfStrings(currentState.firstCustomerPath);
|
|
337
|
+
const pricingHypothesis = normalizedText(currentState.pricingHypothesis);
|
|
338
|
+
const customerOneProfitability = normalizedText(currentState.customerOneProfitability);
|
|
339
|
+
const growthDiscipline = listOfStrings(currentState.growthDiscipline);
|
|
340
|
+
const reversibility = normalizedText(currentState.reversibility);
|
|
341
|
+
const customerTruth = normalizedText(currentState.customerTruth);
|
|
342
|
+
const valuesFit = normalizedText(currentState.valuesFit);
|
|
267
343
|
const toBe = normalizedText(currentState.toBe);
|
|
268
344
|
const whyNow = normalizedText(currentState.whyNow);
|
|
269
345
|
const nextStep = normalizedText(currentState.nextStep);
|
|
@@ -284,12 +360,21 @@ function buildBrainstormReport({ currentState, benchmark, knowledge, workspaceSc
|
|
|
284
360
|
...(community.length > 0
|
|
285
361
|
? [`第一批最容易先验证的人群或社区是:${community.slice(0, 2).join('、')}。`]
|
|
286
362
|
: ['当前还没锁定第一批最容易触达的人群或社区,建议先补最现实的反馈入口。']),
|
|
363
|
+
...(communityFit.length > 0
|
|
364
|
+
? [`你现在更适合先从这里切入:${communityFit.slice(0, 2).join('、')}。`]
|
|
365
|
+
: ['当前还没证明你为什么算这个社区里的自己人,建议先补“为什么现在就能触达”。']),
|
|
287
366
|
...(currentAlternative
|
|
288
367
|
? [`现在主要还是靠“${currentAlternative}”这类办法在撑着,先判断值不值得替换。`]
|
|
289
368
|
: ['当前还没明确现在是怎么解决的,建议先补现有替代方案。']),
|
|
369
|
+
...(painEvidence.length > 0
|
|
370
|
+
? [`这个问题之所以值得做,主要证据是:${painEvidence.slice(0, 2).join(';')}。`]
|
|
371
|
+
: ['当前还没证明这个问题到底有多痛,建议先补真实痛点和现有成本。']),
|
|
290
372
|
...(whyNow
|
|
291
373
|
? [`现在提这件事,主要因为:${whyNow}`]
|
|
292
374
|
: ['为什么现在要做还不够具体,建议先说清触发这件事的变化。']),
|
|
375
|
+
...(mvpSlice
|
|
376
|
+
? [`如果现在就动手,第一版最好只做“${mvpSlice}”这一件事。`]
|
|
377
|
+
: ['当前还没把第一版压成“一件事 MVP”,建议继续收范围。']),
|
|
293
378
|
];
|
|
294
379
|
const directionOptions = [
|
|
295
380
|
`推荐方向:${goalsDirection(currentState, topic)}。`,
|
|
@@ -301,6 +386,12 @@ function buildBrainstormReport({ currentState, benchmark, knowledge, workspaceSc
|
|
|
301
386
|
...(manualPath.length > 0
|
|
302
387
|
? [`不等完整产品也可以先这样交付价值:${manualPath.slice(0, 2).join('、')}。`]
|
|
303
388
|
: ['如果还没准备好完整产品,建议先补一条手工服务或半自动交付路径。']),
|
|
389
|
+
...(weekendTest
|
|
390
|
+
? [`如果要压轻一点,可以先跑这样一个周末级验证:${weekendTest}。`]
|
|
391
|
+
: ['建议再压一层,先想清楚有没有周末级 MVP 或更轻的试跑方式。']),
|
|
392
|
+
...(smallestExecution.length > 0
|
|
393
|
+
? [`更小的第一版可以先这样跑:${smallestExecution.slice(0, 2).join('、')}。`]
|
|
394
|
+
: ['建议优先想清楚能不能先用 spreadsheet、表单或 no-code 跑起来;如果必须开始做产品,也只自动化最重复的一步,先压成 forms / lists / CRUD 骨架,不要先为假想中的未来客户造复杂能力。']),
|
|
304
395
|
...(inScope.length > 0
|
|
305
396
|
? [`这轮先聚焦:${inScope.slice(0, 2).join('、')}。`]
|
|
306
397
|
: ['范围边界还不够清楚,建议先补“这轮先做什么、先不做什么”。']),
|
|
@@ -312,6 +403,12 @@ function buildBrainstormReport({ currentState, benchmark, knowledge, workspaceSc
|
|
|
312
403
|
: []),
|
|
313
404
|
];
|
|
314
405
|
const validationPlan = [
|
|
406
|
+
...(communityFit.length > 0
|
|
407
|
+
? communityFit.slice(0, 2).map((item) => `社区契合:${item}`)
|
|
408
|
+
: ['建议先证明你为什么算这个社区里的自己人、他们在哪里聚集、为什么这个切口够小,以及你现在是否已经在真实参与或贡献。']),
|
|
409
|
+
...(manualPlaybook.length > 0
|
|
410
|
+
? manualPlaybook.slice(0, 2).map((item) => `手工作战卡:${item}`)
|
|
411
|
+
: ['建议先把手工作战卡写出来,至少补齐触发条件、步骤、工具、耗时和交接点。']),
|
|
315
412
|
...(commitmentSignals.length > 0
|
|
316
413
|
? commitmentSignals.slice(0, 2).map((item) => `先看真实承诺:${item}`)
|
|
317
414
|
: ['建议先定义什么真实承诺能证明这不是口头兴趣。']),
|
|
@@ -326,6 +423,15 @@ function buildBrainstormReport({ currentState, benchmark, knowledge, workspaceSc
|
|
|
326
423
|
: openQuestions.length > 0
|
|
327
424
|
? [`低成本先验证最不确定的问题:${openQuestions[0]}`]
|
|
328
425
|
: ['建议先定一个最低成本的验证动作,再决定是否进入完整 PRD。']),
|
|
426
|
+
...(paymentProof.length > 0
|
|
427
|
+
? paymentProof.slice(0, 2).map((item) => `付费验证:${item}`)
|
|
428
|
+
: ['建议先补有没有 10 个样本、3/10 付费意愿或更强交易信号。']),
|
|
429
|
+
...(weekendTest
|
|
430
|
+
? [`再压一层可以先做:${weekendTest}`]
|
|
431
|
+
: ['如果还太重,建议继续把验证动作压到周末级别。']),
|
|
432
|
+
...(productizeGate.length > 0
|
|
433
|
+
? productizeGate.slice(0, 2).map((item) => `产品化门槛:${item}`)
|
|
434
|
+
: ['建议先写清达到什么条件才允许继续产品化,而不是一有想法就继续扩。']),
|
|
329
435
|
...(stopLossActions.length > 0
|
|
330
436
|
? stopLossActions.slice(0, 2).map((item) => `如果验证不顺,先这样止损:${item}`)
|
|
331
437
|
: ['如果验证结果不达预期,最好提前定义什么情况下先停。']),
|
|
@@ -334,26 +440,67 @@ function buildBrainstormReport({ currentState, benchmark, knowledge, workspaceSc
|
|
|
334
440
|
...(community.length > 0
|
|
335
441
|
? [`先去这里找真实反馈:${community.slice(0, 2).join('、')}。`]
|
|
336
442
|
: ['先补第一批最容易触达的社区、渠道或人群。']),
|
|
443
|
+
...(communityFit.length > 0
|
|
444
|
+
? [`你更像这个圈子的自己人,因为:${communityFit.slice(0, 2).join('、')}。`]
|
|
445
|
+
: ['先补你为什么算这个社区里的自己人、他们为什么愿意先相信你。']),
|
|
337
446
|
...(seedUsers.length > 0
|
|
338
447
|
? [`第一批优先先找:${seedUsers.slice(0, 2).join('、')}。`]
|
|
339
448
|
: ['先补第一批最值得先聊、先服务的人。']),
|
|
340
449
|
...(currentAlternative
|
|
341
450
|
? [`当前主要替代方案是:${currentAlternative}。`]
|
|
342
451
|
: ['先补用户现在主要靠什么办法在解决。']),
|
|
452
|
+
...(painEvidence.length > 0
|
|
453
|
+
? [`痛点证据更像这样:${painEvidence.slice(0, 2).join(';')}。`]
|
|
454
|
+
: ['先补这个问题到底有多痛,用户现在已经在付出什么时间或金钱。']),
|
|
343
455
|
...(manualPath.length > 0
|
|
344
456
|
? [`先不做完整产品时,也可以这样手工交付:${manualPath.slice(0, 2).join('、')}。`]
|
|
345
457
|
: ['先补一条不做完整产品也能交付价值的手工路径。']),
|
|
458
|
+
...(manualPlaybook.length > 0
|
|
459
|
+
? manualPlaybook.slice(0, 2).map((item) => `手工作战卡先这样写:${item}`)
|
|
460
|
+
: ['先补一版手工作战卡,把触发条件、步骤、工具、耗时和交接点写清楚。']),
|
|
461
|
+
...(firstCustomerPath.length > 0
|
|
462
|
+
? [`第一批客户更现实的顺序是:${firstCustomerPath.slice(0, 3).join(' -> ')}。`]
|
|
463
|
+
: ['先补第一批客户最现实的触达顺序,比如熟人、社区和陌生线索怎么排。']),
|
|
346
464
|
];
|
|
347
465
|
const businessViability = [
|
|
348
466
|
...(commitmentSignals.length > 0
|
|
349
467
|
? commitmentSignals.slice(0, 2).map((item) => `先用这种承诺证明值得继续:${item}`)
|
|
350
468
|
: ['先定义什么真实承诺最能证明值得继续。']),
|
|
469
|
+
...(paymentProof.length > 0
|
|
470
|
+
? paymentProof.slice(0, 2).map((item) => `付费证明:${item}`)
|
|
471
|
+
: ['先补 10 个样本里有没有 3/10 愿意付费、预付或给出更强承诺。']),
|
|
472
|
+
...(pricingHypothesis
|
|
473
|
+
? [`从第一个客户开始,先按这个方式收费:${pricingHypothesis}`]
|
|
474
|
+
: ['先给一个从第一个客户开始就成立的收费假设,不要无限期免费。']),
|
|
475
|
+
...(customerOneProfitability
|
|
476
|
+
? [`客户 1 的盈利或打平路径:${customerOneProfitability}`]
|
|
477
|
+
: ['先判断第一个客户怎么覆盖时间和交付成本。']),
|
|
351
478
|
...(defaultAlivePlan.length > 0
|
|
352
479
|
? defaultAlivePlan.slice(0, 2).map((item) => `先活下来要守住:${item}`)
|
|
353
480
|
: ['先补验证阶段怎样控制成本、时间和交付方式,确保这件事先活下来。']),
|
|
354
481
|
...(firstValidationStep
|
|
355
482
|
? [`下一步最便宜的验证动作是:${firstValidationStep}`]
|
|
356
483
|
: ['先定一个最低成本验证动作,再决定要不要继续做大。']),
|
|
484
|
+
...(growthDiscipline.length > 0
|
|
485
|
+
? growthDiscipline.slice(0, 2).map((item) => `增长纪律:${item}`)
|
|
486
|
+
: ['先约束增长纪律:先卖再 launch,先花时间别先花钱。']),
|
|
487
|
+
];
|
|
488
|
+
const minimalistReview = [
|
|
489
|
+
...(smallestExecution.length > 0
|
|
490
|
+
? smallestExecution.slice(0, 2).map((item) => `更小的执行方式:${item}`)
|
|
491
|
+
: ['先判断能不能继续保持小:先用 spreadsheet、表单或 no-code 跑;如果必须开始做产品,也只自动化最重复的一步,先压成 forms / lists / CRUD 骨架,不要先为假想中的未来客户造复杂能力。']),
|
|
492
|
+
...(productizeGate.length > 0
|
|
493
|
+
? productizeGate.slice(0, 2).map((item) => `产品化条件:${item}`)
|
|
494
|
+
: ['先写清达到什么条件才允许产品化,别让功能冲动替代真实门槛。']),
|
|
495
|
+
...(reversibility
|
|
496
|
+
? [`如果结果一般,这条路的可逆性是:${reversibility}`]
|
|
497
|
+
: ['先判断如果验证结果一般,这条路是不是容易回退,避免先做重招聘、长期绑定或重平台化。']),
|
|
498
|
+
...(customerTruth
|
|
499
|
+
? [`客户真问题校验:${customerTruth}`]
|
|
500
|
+
: ['先判断这更像在解决客户真问题,还是团队自己的技术冲动。']),
|
|
501
|
+
...(valuesFit
|
|
502
|
+
? [`价值观一致性:${valuesFit}`]
|
|
503
|
+
: ['先确认这条路是否符合团队现在想坚持的价值观和长期经营方式;如果连续这样做 3 到 5 年,你还愿意住在这套业务里吗?']),
|
|
357
504
|
];
|
|
358
505
|
const reuseFoundation = [
|
|
359
506
|
...(inScope.length > 0
|
|
@@ -395,6 +542,7 @@ function buildBrainstormReport({ currentState, benchmark, knowledge, workspaceSc
|
|
|
395
542
|
marketSignals: directionOptions.slice(0, 8),
|
|
396
543
|
validationLoop: validationLoop.slice(0, 8),
|
|
397
544
|
businessViability: businessViability.slice(0, 8),
|
|
545
|
+
minimalistReview: minimalistReview.slice(0, 8),
|
|
398
546
|
risks: validationPlan.slice(0, 8),
|
|
399
547
|
reuseOpportunities: reuseFoundation.slice(0, 8),
|
|
400
548
|
openQuestions: openQuestions.slice(0, 8),
|
|
@@ -524,6 +672,8 @@ export async function brainstormWorkspace(projectRoot, options = {}) {
|
|
|
524
672
|
stakeholders: listOfStrings(currentState.stakeholders),
|
|
525
673
|
community: listOfStrings(currentState.community),
|
|
526
674
|
seedUsers: listOfStrings(currentState.seedUsers),
|
|
675
|
+
communityFit: listOfStrings(currentState.communityFit),
|
|
676
|
+
painEvidence: listOfStrings(currentState.painEvidence),
|
|
527
677
|
goals: listOfStrings(currentState.goals),
|
|
528
678
|
successMetrics: listOfStrings(currentState.successMetrics),
|
|
529
679
|
inScope: listOfStrings(currentState.inScope),
|
|
@@ -534,9 +684,22 @@ export async function brainstormWorkspace(projectRoot, options = {}) {
|
|
|
534
684
|
stopLossActions: listOfStrings(currentState.stopLossActions),
|
|
535
685
|
currentAlternative: currentState.currentAlternative ?? '',
|
|
536
686
|
manualPath: listOfStrings(currentState.manualPath),
|
|
687
|
+
manualPlaybook: listOfStrings(currentState.manualPlaybook),
|
|
537
688
|
commitmentSignals: listOfStrings(currentState.commitmentSignals),
|
|
538
689
|
firstValidationStep: currentState.firstValidationStep ?? '',
|
|
539
690
|
defaultAlivePlan: listOfStrings(currentState.defaultAlivePlan),
|
|
691
|
+
paymentProof: listOfStrings(currentState.paymentProof),
|
|
692
|
+
mvpSlice: currentState.mvpSlice ?? '',
|
|
693
|
+
weekendTest: currentState.weekendTest ?? '',
|
|
694
|
+
smallestExecution: listOfStrings(currentState.smallestExecution),
|
|
695
|
+
productizeGate: listOfStrings(currentState.productizeGate),
|
|
696
|
+
firstCustomerPath: listOfStrings(currentState.firstCustomerPath),
|
|
697
|
+
pricingHypothesis: currentState.pricingHypothesis ?? '',
|
|
698
|
+
customerOneProfitability: currentState.customerOneProfitability ?? '',
|
|
699
|
+
growthDiscipline: listOfStrings(currentState.growthDiscipline),
|
|
700
|
+
reversibility: currentState.reversibility ?? '',
|
|
701
|
+
customerTruth: currentState.customerTruth ?? '',
|
|
702
|
+
valuesFit: currentState.valuesFit ?? '',
|
|
540
703
|
asIs: currentState.asIs ?? '',
|
|
541
704
|
toBe: currentState.toBe ?? '',
|
|
542
705
|
nextStep: currentState.nextStep ?? '',
|
package/src/cli/quality-print.js
CHANGED
|
@@ -204,20 +204,26 @@ function printKnowledgeResult(result, json) {
|
|
|
204
204
|
const counts = result.counts ?? {};
|
|
205
205
|
console.log(`OpenPrd knowledge candidates: ${result.candidates.length} 个 (${result.status})`);
|
|
206
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
207
|
+
const ordered = [...result.candidates].sort((left, right) => (right.occurrences ?? 1) - (left.occurrences ?? 1));
|
|
208
|
+
for (const candidate of ordered) {
|
|
209
|
+
const occurrences = candidate.occurrences ?? 1;
|
|
210
|
+
const repeatNote = occurrences > 1 ? `(同类经验已重复出现 ${occurrences} 次,值得优先保留)` : '';
|
|
211
|
+
console.log(`- ${candidate.title ?? candidate.candidateId}${repeatNote}`);
|
|
212
|
+
console.log(` 编号: ${candidate.candidateId} · 状态: ${candidate.status}`);
|
|
210
213
|
console.log(` 候选: ${candidate.path ?? candidate.files?.candidate}`);
|
|
211
214
|
if (candidate.draftSkillPath ?? candidate.files?.draftSkill) {
|
|
212
215
|
console.log(` 草案 Skill: ${candidate.draftSkillPath ?? candidate.files?.draftSkill}`);
|
|
213
216
|
}
|
|
214
217
|
if (candidate.pending) {
|
|
218
|
+
console.log(` 保留: openprd quality . --learn --review --from ${candidate.files?.candidateDir ?? candidate.candidateId}`);
|
|
215
219
|
console.log(` 拒绝: openprd knowledge reject --id ${candidate.candidateId} --reason <原因>`);
|
|
216
220
|
console.log(` 归档: openprd knowledge archive --id ${candidate.candidateId} --reason <原因>`);
|
|
217
221
|
}
|
|
218
222
|
}
|
|
219
223
|
if (result.candidates.length === 0) {
|
|
220
224
|
console.log('没有匹配的 knowledge candidate。');
|
|
225
|
+
} else if (ordered.some((candidate) => candidate.pending)) {
|
|
226
|
+
console.log('给用户的确认话术: 只要回复「保留第 N 条」或「忽略第 N 条」,Agent 就会执行对应命令,不需要手动操作。');
|
|
221
227
|
}
|
|
222
228
|
return;
|
|
223
229
|
}
|