@gordon.gan/specflow 1.3.3-beta → 1.4.0-beta
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/README.md +6 -2
- package/dist/integrations/shared/capability-evidence.js +37 -1
- package/dist/integrations/shared/command-catalog.js +1 -0
- package/dist/integrations/shared/parity-manifest.js +6 -0
- package/package.json +1 -1
- package/prompts/approval/generate.md +912 -0
- package/skills/specflow-approval/SKILL.md +508 -0
- package/templates/approval.md +390 -0
package/README.md
CHANGED
|
@@ -72,6 +72,8 @@ explore(可选)→ propose → refine → apply → review → test → veri
|
|
|
72
72
|
│ ↓ │
|
|
73
73
|
│ refine ≥2 轮精化:挑战假设 / 新方案 / 探边界 / 质疑 scope │
|
|
74
74
|
│ ↓ │
|
|
75
|
+
│ approval [可选] AI 7 维闭环检查 + 设计质量 + 可实施性 → approval.md 供审批 │
|
|
76
|
+
│ ↓ │
|
|
75
77
|
│ apply Phase A 重写 tasks.md → Phase B 逐任务 TDD 执行 │
|
|
76
78
|
│ (可选 /specflow:apply --yes:少打断,成功后串行验收) │
|
|
77
79
|
│ ↓ │
|
|
@@ -124,7 +126,7 @@ npm install -g @gordon.gan/specflow
|
|
|
124
126
|
npm install -g github:Gordon-Gan-Jiang/specflow
|
|
125
127
|
|
|
126
128
|
# 验证
|
|
127
|
-
specflow --version # 以 npm / package.json 为准(当前 1.
|
|
129
|
+
specflow --version # 以 npm / package.json 为准(当前 1.4.0-beta)
|
|
128
130
|
specflow --help
|
|
129
131
|
```
|
|
130
132
|
|
|
@@ -257,13 +259,14 @@ specflow init
|
|
|
257
259
|
|
|
258
260
|
## IDE 工作流命令
|
|
259
261
|
|
|
260
|
-
共 **
|
|
262
|
+
共 **11 个**技能,命名因 IDE 而异:
|
|
261
263
|
|
|
262
264
|
| 阶段 | Claude Code | Cursor | Codex |
|
|
263
265
|
|------|-------------|--------|-------|
|
|
264
266
|
| 探索(可选) | `/specflow:explore` | `specflow:explore` | `$specflow-explore` |
|
|
265
267
|
| 规划 | `/specflow:propose` | `specflow:propose` | `$specflow-propose` |
|
|
266
268
|
| 精化 | `/specflow:refine` | `specflow:refine` | `$specflow-refine` |
|
|
269
|
+
| 审批(可选) | `/specflow:approval` | `specflow:approval` | `$specflow-approval` |
|
|
267
270
|
| 构建 | `/specflow:apply` | `specflow:apply` | `$specflow-apply` |
|
|
268
271
|
| 审查 | `/specflow:review` | `specflow:review` | `$specflow-review` |
|
|
269
272
|
| 测试 | `/specflow:test` | `specflow:test` | `$specflow-test` |
|
|
@@ -279,6 +282,7 @@ specflow init
|
|
|
279
282
|
| **explore** | 读代码、比方案、定边界;产出 `explore.md`,confirmed 后 handoff 到 propose |
|
|
280
283
|
| **propose** | 一次产出 proposal、delta specs、design、tasks(第一轮深度思考,非占位骨架);叙述语言跟 `artifacts.language` |
|
|
281
284
|
| **refine** | 内部多轮循环(≥2 轮,AI 判断收敛);可更新任意 artifact |
|
|
285
|
+
| **approval** | refine 后可选:AI 对四件套做 7 维闭环性检查(含代码落地性、基线对照)+ 设计质量评估 + 可实施性评估,生成 `approval.md` 供人工审批;不改 phase,不阻塞 apply |
|
|
282
286
|
| **apply** | Phase A 重写 tasks.md;Phase B 按语言路由 ECC 审查 + TDD 逐任务执行;可选 `--yes` |
|
|
283
287
|
| **review** | 代码审查,对照 specs 检查回归 |
|
|
284
288
|
| **test** | 单元 + 集成 + E2E + 回归测试 |
|
|
@@ -7,6 +7,11 @@ const MARKER_RULES = [
|
|
|
7
7
|
{ id: 'sub.propose.first_iteration', skill: 'propose', includes: ['first-iteration deep-analysis pass'] },
|
|
8
8
|
{ id: 'sub.refine.multi_round', skill: 'refine', includes: ['auto-multi-round loop', '## LOOP (Stages 2-4)'] },
|
|
9
9
|
{ id: 'sub.refine.challenge_scope', skill: 'refine', includes: ['Challenge Behaviors #3 and #4', 'question scope'] },
|
|
10
|
+
{ id: 'sub.approval.closed_loop', skill: 'approval', includes: ['Closed-Loop Verification', 'Pass 1'] },
|
|
11
|
+
{ id: 'sub.approval.implementability', skill: 'approval', includes: ['Implementability Assessment', 'Completeness'] },
|
|
12
|
+
{ id: 'sub.approval.no_phase_change', skill: 'approval', includes: ['does NOT advance phase', 'phase stays `refined`'] },
|
|
13
|
+
{ id: 'sub.approval.code_grounding', skill: 'approval', includes: ['Code Grounding', 'Pass 6'] },
|
|
14
|
+
{ id: 'sub.approval.design_quality', skill: 'approval', includes: ['Design Quality Assessment', 'Over-Engineering'] },
|
|
10
15
|
{ id: 'sub.apply.phase_a', skill: 'apply', includes: ['## Phase A: Task Rewrite'] },
|
|
11
16
|
{ id: 'sub.apply.phase_b', skill: 'apply', includes: ['## Phase B: Subagent TDD Execution'] },
|
|
12
17
|
{ id: 'sub.apply.gap_detection', skill: 'apply', includes: ['Gap Detection'] },
|
|
@@ -19,8 +24,39 @@ const MARKER_RULES = [
|
|
|
19
24
|
{ id: 'failure.explore.proposal_exists_redirect', skill: 'explore', includes: ['explore is too late', 'refine'] },
|
|
20
25
|
{ id: 'failure.propose.explore_draft_gate', skill: 'propose', includes: ['Status: draft', 'REFUSE to proceed'] },
|
|
21
26
|
{ id: 'failure.apply.phase_gate', skill: 'apply', includes: ['HARD GATE (prerequisite)', 'phase is not `refined`, REFUSE'] },
|
|
27
|
+
{ id: 'failure.approval.phase_gate', skill: 'approval', includes: ['phase must be `refined`', 'REFUSE to proceed'] },
|
|
22
28
|
{ id: 'failure.verify.explicit_skip_marker', skill: 'verify', includes: ['Pass 2: skipped (no baseline'] },
|
|
23
29
|
];
|
|
30
|
+
/**
|
|
31
|
+
* Checks whether a marker phrase appears in `content` as an independent token.
|
|
32
|
+
*
|
|
33
|
+
* Uses explicit character-class boundaries (not regex `\b`) so phrases that
|
|
34
|
+
* contain punctuation (e.g. "## LOOP (Stages 2-4)", "Pass 2: skipped (no
|
|
35
|
+
* baseline") are matched reliably, while a longer word that merely *contains*
|
|
36
|
+
* the phrase (e.g. "Code GroundingXX" vs "Code Grounding") is NOT treated as
|
|
37
|
+
* a match. This prevents capability evidence from false-positive on accidental
|
|
38
|
+
* substrings.
|
|
39
|
+
*/
|
|
40
|
+
function containsIndependentPhrase(content, phrase) {
|
|
41
|
+
if (phrase.length === 0) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
const boundary = /[A-Za-z0-9_]/;
|
|
45
|
+
const escaped = phrase.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
46
|
+
const re = new RegExp(escaped, 'g');
|
|
47
|
+
let match;
|
|
48
|
+
while ((match = re.exec(content)) !== null) {
|
|
49
|
+
const before = match.index > 0 ? content[match.index - 1] : '';
|
|
50
|
+
const afterIndex = match.index + phrase.length;
|
|
51
|
+
const after = afterIndex < content.length ? content[afterIndex] : '';
|
|
52
|
+
const beforeBoundary = before !== '' && boundary.test(before);
|
|
53
|
+
const afterBoundary = after !== '' && boundary.test(after);
|
|
54
|
+
if (!beforeBoundary && !afterBoundary) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
24
60
|
export function deriveCapabilityIds(supportedCommandIds, skillContentsByCommand) {
|
|
25
61
|
const capabilityIds = new Set();
|
|
26
62
|
for (const commandId of supportedCommandIds) {
|
|
@@ -28,7 +64,7 @@ export function deriveCapabilityIds(supportedCommandIds, skillContentsByCommand)
|
|
|
28
64
|
}
|
|
29
65
|
for (const rule of MARKER_RULES) {
|
|
30
66
|
const content = skillContentsByCommand[rule.skill] ?? '';
|
|
31
|
-
const ok = rule.includes.every((needle) => content
|
|
67
|
+
const ok = rule.includes.every((needle) => containsIndependentPhrase(content, needle));
|
|
32
68
|
if (ok) {
|
|
33
69
|
capabilityIds.add(rule.id);
|
|
34
70
|
}
|
|
@@ -2,6 +2,7 @@ export const COMMAND_CATALOG = [
|
|
|
2
2
|
{ id: 'explore', description: 'Think-before-propose exploration when requirements are fuzzy' },
|
|
3
3
|
{ id: 'propose', description: 'Create proposal and spec artifacts for a change' },
|
|
4
4
|
{ id: 'refine', description: 'Refine and iterate on spec artifacts' },
|
|
5
|
+
{ id: 'approval', description: 'Post-refine technical approval document with AI closed-loop check' },
|
|
5
6
|
{ id: 'apply', description: 'Implement tasks from a change spec' },
|
|
6
7
|
{ id: 'review', description: 'Review code changes against spec' },
|
|
7
8
|
{ id: 'test', description: 'Run tests and verify coverage' },
|
|
@@ -13,6 +13,11 @@ export const CAPABILITY_MANIFEST = [
|
|
|
13
13
|
{ id: 'sub.propose.first_iteration', commandId: 'propose', level: 'sub-capability', required: true },
|
|
14
14
|
{ id: 'sub.refine.multi_round', commandId: 'refine', level: 'sub-capability', required: true },
|
|
15
15
|
{ id: 'sub.refine.challenge_scope', commandId: 'refine', level: 'sub-capability', required: true },
|
|
16
|
+
{ id: 'sub.approval.closed_loop', commandId: 'approval', level: 'sub-capability', required: true },
|
|
17
|
+
{ id: 'sub.approval.implementability', commandId: 'approval', level: 'sub-capability', required: true },
|
|
18
|
+
{ id: 'sub.approval.no_phase_change', commandId: 'approval', level: 'sub-capability', required: true },
|
|
19
|
+
{ id: 'sub.approval.code_grounding', commandId: 'approval', level: 'sub-capability', required: true },
|
|
20
|
+
{ id: 'sub.approval.design_quality', commandId: 'approval', level: 'sub-capability', required: true },
|
|
16
21
|
{ id: 'sub.apply.phase_a', commandId: 'apply', level: 'sub-capability', required: true },
|
|
17
22
|
{ id: 'sub.apply.phase_b', commandId: 'apply', level: 'sub-capability', required: true },
|
|
18
23
|
{ id: 'sub.apply.gap_detection', commandId: 'apply', level: 'sub-capability', required: true },
|
|
@@ -25,6 +30,7 @@ export const CAPABILITY_MANIFEST = [
|
|
|
25
30
|
{ id: 'failure.explore.proposal_exists_redirect', commandId: 'explore', level: 'failure-path', required: true },
|
|
26
31
|
{ id: 'failure.propose.explore_draft_gate', commandId: 'propose', level: 'failure-path', required: true },
|
|
27
32
|
{ id: 'failure.apply.phase_gate', commandId: 'apply', level: 'failure-path', required: true },
|
|
33
|
+
{ id: 'failure.approval.phase_gate', commandId: 'approval', level: 'failure-path', required: true },
|
|
28
34
|
{ id: 'failure.verify.explicit_skip_marker', commandId: 'verify', level: 'failure-path', required: true },
|
|
29
35
|
];
|
|
30
36
|
export const IDE_ASSET_MANIFEST = [
|
package/package.json
CHANGED