@gordon.gan/specflow 1.7.0-beta → 1.8.0-alpha

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.
Files changed (151) hide show
  1. package/README.md +4 -4
  2. package/dist/cli/commands/document-run.d.ts +84 -0
  3. package/dist/cli/commands/document-run.js +539 -0
  4. package/dist/cli/index.js +2 -2
  5. package/dist/core/artifact-language.js +11 -1
  6. package/dist/core/document/chapters.d.ts +6 -0
  7. package/dist/core/document/chapters.js +21 -0
  8. package/dist/core/document/coverage.d.ts +25 -0
  9. package/dist/core/document/coverage.js +49 -0
  10. package/dist/core/document/digests.d.ts +41 -0
  11. package/dist/core/document/digests.js +136 -0
  12. package/dist/core/document/engine.d.ts +92 -0
  13. package/dist/core/document/engine.js +487 -0
  14. package/dist/core/{approval → document}/forbidden-patterns.d.ts +4 -2
  15. package/dist/core/document/forbidden-patterns.js +33 -0
  16. package/dist/core/document/gates.d.ts +27 -0
  17. package/dist/core/document/gates.js +33 -0
  18. package/dist/core/document/index.d.ts +7 -0
  19. package/dist/core/document/index.js +7 -0
  20. package/dist/core/document/input-digest.d.ts +55 -0
  21. package/dist/core/document/input-digest.js +121 -0
  22. package/dist/core/document/input-features.d.ts +24 -0
  23. package/dist/core/document/input-features.js +112 -0
  24. package/dist/core/document/lint.d.ts +22 -0
  25. package/dist/core/document/lint.js +29 -0
  26. package/dist/core/document/llm.d.ts +35 -0
  27. package/dist/core/document/llm.js +27 -0
  28. package/dist/core/document/map.d.ts +46 -0
  29. package/dist/core/document/map.js +212 -0
  30. package/dist/core/document/outline.d.ts +26 -0
  31. package/dist/core/document/outline.js +128 -0
  32. package/dist/core/document/paths.d.ts +23 -0
  33. package/dist/core/document/paths.js +31 -0
  34. package/dist/core/document/profile-validator.d.ts +23 -0
  35. package/dist/core/document/profile-validator.js +127 -0
  36. package/dist/core/document/profiles.d.ts +23 -0
  37. package/dist/core/document/profiles.js +69 -0
  38. package/dist/core/document/render.d.ts +19 -0
  39. package/dist/core/document/render.js +97 -0
  40. package/dist/core/document/review.d.ts +42 -0
  41. package/dist/core/document/review.js +157 -0
  42. package/dist/core/document/scene-detect.d.ts +25 -0
  43. package/dist/core/document/scene-detect.js +121 -0
  44. package/dist/core/document/schemas.d.ts +949 -0
  45. package/dist/core/document/schemas.js +158 -0
  46. package/dist/integrations/shared/capability-evidence.js +4 -8
  47. package/dist/integrations/shared/command-catalog.js +1 -1
  48. package/dist/integrations/shared/parity-manifest.js +4 -8
  49. package/package.json +1 -1
  50. package/prompts/apply/phase-a-plan.md +1 -1
  51. package/prompts/document/map/acceptance.md +8 -0
  52. package/prompts/document/map/api-design.md +17 -0
  53. package/prompts/document/map/architecture.md +7 -0
  54. package/prompts/document/map/closed-loop.md +8 -0
  55. package/prompts/document/map/compat-migration.md +8 -0
  56. package/prompts/document/map/config-runtime.md +8 -0
  57. package/prompts/document/map/core-logic.md +8 -0
  58. package/prompts/document/map/data-model.md +17 -0
  59. package/prompts/document/map/deploy.md +8 -0
  60. package/prompts/document/map/fix.md +7 -0
  61. package/prompts/document/map/goal.md +7 -0
  62. package/prompts/document/map/impact.md +7 -0
  63. package/prompts/document/map/implementability.md +8 -0
  64. package/prompts/document/map/mvp-boundary.md +7 -0
  65. package/prompts/document/map/non-goals.md +7 -0
  66. package/prompts/document/map/regression.md +7 -0
  67. package/prompts/document/map/reproduce.md +7 -0
  68. package/prompts/document/map/requirement.md +7 -0
  69. package/prompts/document/map/root-cause.md +7 -0
  70. package/prompts/document/map/signoff.md +8 -0
  71. package/prompts/document/map/tech-selection.md +17 -0
  72. package/prompts/document/map/test-strategy.md +7 -0
  73. package/prompts/document/map/ui-design.md +18 -0
  74. package/prompts/document/outline/general.md +37 -0
  75. package/prompts/document/review/ai-review.md +56 -0
  76. package/prompts/explore/explore-session.md +1 -1
  77. package/prompts/propose/design-draft.md +1 -1
  78. package/prompts/propose/proposal.md +1 -1
  79. package/prompts/propose/specs.md +1 -1
  80. package/prompts/propose/tasks-draft.md +1 -1
  81. package/prompts/refine/design-output.md +1 -1
  82. package/prompts/refine/update-artifacts.md +2 -0
  83. package/prompts/shared/artifact-language.md +13 -0
  84. package/skills/specflow-document/SKILL.md +124 -0
  85. package/templates/document/chapters/acceptance.yaml +16 -0
  86. package/templates/document/chapters/api-design.yaml +24 -0
  87. package/templates/document/chapters/architecture.yaml +18 -0
  88. package/templates/document/chapters/closed-loop.yaml +22 -0
  89. package/templates/document/chapters/compat-migration.yaml +17 -0
  90. package/templates/document/chapters/config-runtime.yaml +16 -0
  91. package/templates/document/chapters/core-logic.yaml +16 -0
  92. package/templates/document/chapters/data-model.yaml +25 -0
  93. package/templates/document/chapters/deploy.yaml +18 -0
  94. package/templates/document/chapters/fix.yaml +18 -0
  95. package/templates/document/chapters/goal.yaml +17 -0
  96. package/templates/document/chapters/impact.yaml +16 -0
  97. package/templates/document/chapters/implementability.yaml +22 -0
  98. package/templates/document/chapters/mvp-boundary.yaml +20 -0
  99. package/templates/document/chapters/non-goals.yaml +17 -0
  100. package/templates/document/chapters/regression.yaml +16 -0
  101. package/templates/document/chapters/reproduce.yaml +16 -0
  102. package/templates/document/chapters/requirement.yaml +18 -0
  103. package/templates/document/chapters/root-cause.yaml +18 -0
  104. package/templates/document/chapters/signoff.yaml +15 -0
  105. package/templates/document/chapters/tech-selection.yaml +20 -0
  106. package/templates/document/chapters/test-strategy.yaml +20 -0
  107. package/templates/document/chapters/ui-design.yaml +22 -0
  108. package/templates/document/profiles/0to1.yaml +12 -0
  109. package/templates/document/profiles/approve.yaml +22 -0
  110. package/templates/document/profiles/bugfix.yaml +10 -0
  111. package/templates/document/profiles/feature.yaml +11 -0
  112. package/dist/cli/commands/approval-assemble.d.ts +0 -64
  113. package/dist/cli/commands/approval-assemble.js +0 -408
  114. package/dist/core/approval/assemble.d.ts +0 -10
  115. package/dist/core/approval/assemble.js +0 -384
  116. package/dist/core/approval/bundle.d.ts +0 -9
  117. package/dist/core/approval/bundle.js +0 -172
  118. package/dist/core/approval/forbidden-patterns.js +0 -37
  119. package/dist/core/approval/index-schema.d.ts +0 -676
  120. package/dist/core/approval/index-schema.js +0 -193
  121. package/dist/core/approval/index.d.ts +0 -12
  122. package/dist/core/approval/index.js +0 -9
  123. package/dist/core/approval/lint.d.ts +0 -10
  124. package/dist/core/approval/lint.js +0 -302
  125. package/dist/core/approval/paths.d.ts +0 -13
  126. package/dist/core/approval/paths.js +0 -43
  127. package/dist/core/approval/pipeline.d.ts +0 -28
  128. package/dist/core/approval/pipeline.js +0 -146
  129. package/dist/core/approval/playbook-schema.d.ts +0 -182
  130. package/dist/core/approval/playbook-schema.js +0 -51
  131. package/dist/core/approval/render.d.ts +0 -20
  132. package/dist/core/approval/render.js +0 -210
  133. package/dist/core/approval/review-pack.d.ts +0 -26
  134. package/dist/core/approval/review-pack.js +0 -205
  135. package/dist/core/approval/types.d.ts +0 -223
  136. package/dist/core/approval/types.js +0 -1
  137. package/prompts/approval/acp-pipeline.md +0 -104
  138. package/prompts/approval/ai-review.md +0 -145
  139. package/prompts/approval/api-guidance.md +0 -179
  140. package/prompts/approval/database-guidance.md +0 -105
  141. package/prompts/approval/frontend-guidance.md +0 -249
  142. package/prompts/approval/generate.md +0 -1583
  143. package/prompts/approval/multi-repo-guidance.md +0 -238
  144. package/prompts/approval/project-conventions-guidance.md +0 -171
  145. package/prompts/approval/runtime-guidance.md +0 -64
  146. package/prompts/approval/segmented-generation.md +0 -157
  147. package/skills/specflow-approval/SKILL.md +0 -663
  148. package/templates/approval-index.yaml +0 -93
  149. package/templates/approval-part.md +0 -15
  150. package/templates/approval-playbook.yaml +0 -28
  151. package/templates/approval.md +0 -261
@@ -0,0 +1,22 @@
1
+ # UI 设计(feature 候选;依赖接口)
2
+ id: ui-design
3
+ title: 前端/UI 设计
4
+ when: input.uiInScope
5
+ depends_on: [api-design]
6
+ outline_points:
7
+ - { id: UI1, text: 页面/路由清单 + 栈五元组(G5), required: true, kind: mixed }
8
+ - { id: UI2, text: 关键页空态/加载/错误三项(G6), required: true, kind: narrative }
9
+ - { id: UI3, text: 状态与数据获取方案, required: true, kind: narrative }
10
+ - { id: UI4, text: 至少 1 条「本迭代不选/不适用」, required: true, kind: narrative }
11
+ entities:
12
+ decisions:
13
+ required: [id, text]
14
+ narratives:
15
+ ui_notes: required
16
+ gates:
17
+ - uiInScope 时必须给页面清单 + 五元组(G5)
18
+ - 关键页必须写空态/加载/错误三项(G6)
19
+ - 禁止臆造页面树
20
+ - 性能/XSS/埋点仅 design 点名时才写
21
+ map_prompt: prompts/document/map/ui-design.md
22
+ output_budget_tokens: 6000
@@ -0,0 +1,12 @@
1
+ # 0→1 项目方案(决策 3)
2
+ id: 0to1
3
+ name: 0→1 项目方案
4
+ required: [goal, tech-selection, mvp-boundary, non-goals]
5
+ optional_candidates:
6
+ - signoff
7
+ - architecture
8
+ - api-design
9
+ - data-model
10
+ - test-strategy
11
+ forbidden: [reproduce, root-cause, impact, fix, regression, requirement, ui-design]
12
+ shared: []
@@ -0,0 +1,22 @@
1
+ # 审批文档场景(四件套 → 审批文档,对齐 approval 能力)
2
+ id: approve
3
+ name: 审批文档(四件套)
4
+ required:
5
+ - goal # 绪论与边界
6
+ - tech-selection # 技术方案评估(选型/决策/风险/质量)
7
+ - architecture # 架构整体设计
8
+ - api-design # 接口设计(含对外/跨端)
9
+ - data-model # 数据结构/模型变更
10
+ - test-strategy # 测试策略
11
+ - closed-loop # 闭环性检查(7 Pass)
12
+ - implementability # 可实施性评估(7 维)
13
+ optional_candidates:
14
+ - ui-design # 前端/UI(条件: uiInScope)
15
+ - core-logic # 核心算法/逻辑
16
+ - config-runtime # 配置与运行环境
17
+ - compat-migration # 兼容性与迁移
18
+ - acceptance # 验收标准
19
+ - deploy # 部署/发布/回滚
20
+ - signoff # 审批意见(签字栏)
21
+ forbidden: [reproduce, root-cause, impact, fix, regression]
22
+ shared: []
@@ -0,0 +1,10 @@
1
+ # 修 bug 方案(决策 3)
2
+ id: bugfix
3
+ name: 修 bug 方案
4
+ required: [reproduce, root-cause, fix]
5
+ optional_candidates:
6
+ - impact
7
+ - regression
8
+ - signoff
9
+ forbidden: [tech-selection, mvp-boundary, non-goals, goal, api-design, data-model, ui-design, requirement]
10
+ shared: []
@@ -0,0 +1,11 @@
1
+ # 功能开发方案(决策 3)
2
+ id: feature
3
+ name: 功能开发方案
4
+ required: [requirement, test-strategy]
5
+ optional_candidates:
6
+ - signoff
7
+ - api-design
8
+ - data-model
9
+ - ui-design
10
+ forbidden: [reproduce, root-cause, impact, fix, regression, goal, tech-selection, mvp-boundary, non-goals]
11
+ shared: []
@@ -1,64 +0,0 @@
1
- /**
2
- * CLI: specflow approval assemble | check | lint | bundle | review-pack | review-check | finalize
3
- *
4
- * ACP (Approval Compiler Pipeline) — deterministic Reduce/Verify; Map is LLM in skill.
5
- * Prefer `specflow approval finalize` for the full Verify → Reduce → bundle chain.
6
- */
7
- import type { Command } from 'commander';
8
- export interface ApprovalCommandOptions {
9
- readonly store?: string;
10
- readonly cwd?: string;
11
- readonly force?: boolean;
12
- readonly json?: boolean;
13
- readonly noLazyCheck?: boolean;
14
- readonly strict?: boolean;
15
- readonly playbook?: string;
16
- readonly workspaceRoot?: string;
17
- readonly dryRun?: boolean;
18
- readonly requireReview?: boolean;
19
- readonly skipReview?: boolean;
20
- readonly bundle?: boolean;
21
- readonly noBundle?: boolean;
22
- readonly render?: boolean;
23
- readonly renderInput?: string;
24
- readonly renderOutput?: string;
25
- }
26
- export declare function runApprovalAssemble(changeName: string, options: ApprovalCommandOptions & {
27
- checkOnly?: boolean;
28
- }): Promise<{
29
- exitCode: number;
30
- payload: unknown;
31
- }>;
32
- export declare function runApprovalLint(changeName: string, options: ApprovalCommandOptions): Promise<{
33
- exitCode: number;
34
- payload: unknown;
35
- }>;
36
- export declare function runApprovalBundle(changeName: string, options: ApprovalCommandOptions): Promise<{
37
- exitCode: number;
38
- payload: unknown;
39
- }>;
40
- export declare function runApprovalReviewPack(changeName: string, options: ApprovalCommandOptions): Promise<{
41
- exitCode: number;
42
- payload: unknown;
43
- }>;
44
- export declare function runApprovalReviewCheck(changeName: string, options: ApprovalCommandOptions): Promise<{
45
- exitCode: number;
46
- payload: unknown;
47
- }>;
48
- export declare function runApprovalReviewHashes(changeName: string, options: ApprovalCommandOptions): Promise<{
49
- exitCode: number;
50
- payload: unknown;
51
- }>;
52
- export declare function runApprovalRender(changeName: string, options: ApprovalCommandOptions & {
53
- input?: string;
54
- output?: string;
55
- title?: string;
56
- }): Promise<{
57
- exitCode: number;
58
- payload: unknown;
59
- }>;
60
- export declare function runApprovalFinalize(changeName: string, options: ApprovalCommandOptions): Promise<{
61
- exitCode: number;
62
- payload: unknown;
63
- }>;
64
- export declare function registerApprovalAssembleCommand(program: Command): void;
@@ -1,408 +0,0 @@
1
- /**
2
- * CLI: specflow approval assemble | check | lint | bundle | review-pack | review-check | finalize
3
- *
4
- * ACP (Approval Compiler Pipeline) — deterministic Reduce/Verify; Map is LLM in skill.
5
- * Prefer `specflow approval finalize` for the full Verify → Reduce → bundle chain.
6
- */
7
- import { promises as fs } from 'node:fs';
8
- import { addStoreOption, resolveRootFromCommandOptions } from '../shared/store-option.js';
9
- import { validateChangeName } from '../../utils/change-utils.js';
10
- import { assembleApprovalDocument, bundleApprovalDocument, checkApprovalReview, generateReviewPacket, getApprovalOutputPath, getChangeDirectory, lintApprovalDocument, finalizeApprovalDocument, computeReviewPartHashes, readApprovalIndex, renderApprovalDocument, resolveApprovalRenderPaths, resolvePlaybookPath, } from '../../core/approval/index.js';
11
- function printDiagnostics(diagnostics) {
12
- for (const d of diagnostics) {
13
- const prefix = d.severity === 'error' ? 'ERROR' : 'WARN';
14
- const part = d.part ? ` [${d.part}]` : '';
15
- console.error(`${prefix}${part}: ${d.message}`);
16
- }
17
- }
18
- export async function runApprovalAssemble(changeName, options) {
19
- validateChangeName(changeName);
20
- const projectRoot = await resolveRootFromCommandOptions({
21
- store: options.store,
22
- cwd: options.cwd,
23
- });
24
- const changeDir = getChangeDirectory(projectRoot, changeName);
25
- try {
26
- await fs.access(changeDir);
27
- }
28
- catch {
29
- throw new Error(`Change "${changeName}" not found at ${changeDir}`);
30
- }
31
- if (options.requireReview) {
32
- const reviewCheck = await checkApprovalReview({
33
- changeDir,
34
- requirePass: true,
35
- forceRequire: true,
36
- });
37
- if (!reviewCheck.ok) {
38
- const payload = { ok: false, stage: 'review-check', diagnostics: reviewCheck.diagnostics };
39
- if (options.json) {
40
- return { exitCode: 1, payload };
41
- }
42
- printDiagnostics(reviewCheck.diagnostics);
43
- return { exitCode: 1, payload };
44
- }
45
- }
46
- const result = await assembleApprovalDocument({
47
- changeDir,
48
- checkOnly: options.checkOnly ?? false,
49
- force: options.force ?? false,
50
- validateLazy: !options.noLazyCheck,
51
- });
52
- const payload = {
53
- ok: result.ok,
54
- change: changeName,
55
- output: result.outputPath ?? getApprovalOutputPath(changeDir),
56
- check_only: options.checkOnly ?? false,
57
- diagnostics: result.diagnostics,
58
- };
59
- if (options.json) {
60
- return { exitCode: result.ok ? 0 : 1, payload };
61
- }
62
- if (result.diagnostics.length > 0) {
63
- printDiagnostics(result.diagnostics);
64
- }
65
- if (result.ok) {
66
- if (options.checkOnly) {
67
- console.info(`Check passed for change "${changeName}" (parts ready to assemble).`);
68
- }
69
- else {
70
- console.info(`Wrote ${result.outputPath}`);
71
- }
72
- return { exitCode: 0, payload };
73
- }
74
- return { exitCode: 1, payload };
75
- }
76
- export async function runApprovalLint(changeName, options) {
77
- validateChangeName(changeName);
78
- const projectRoot = await resolveRootFromCommandOptions({
79
- store: options.store,
80
- cwd: options.cwd,
81
- });
82
- const changeDir = getChangeDirectory(projectRoot, changeName);
83
- const result = await lintApprovalDocument({
84
- changeDir,
85
- strict: options.strict ?? false,
86
- });
87
- const payload = { ok: result.ok, change: changeName, diagnostics: result.diagnostics };
88
- if (options.json) {
89
- return { exitCode: result.ok ? 0 : 1, payload };
90
- }
91
- printDiagnostics(result.diagnostics);
92
- if (result.ok) {
93
- console.info(`Lint passed for change "${changeName}".`);
94
- return { exitCode: 0, payload };
95
- }
96
- return { exitCode: 1, payload };
97
- }
98
- export async function runApprovalBundle(changeName, options) {
99
- validateChangeName(changeName);
100
- const projectRoot = await resolveRootFromCommandOptions({
101
- store: options.store,
102
- cwd: options.cwd,
103
- });
104
- const workspaceRoot = options.workspaceRoot ?? projectRoot;
105
- const changeDir = getChangeDirectory(projectRoot, changeName);
106
- const playbookPath = await resolvePlaybookPath(changeDir, options.playbook);
107
- const result = await bundleApprovalDocument({
108
- workspaceRoot,
109
- changeDir,
110
- playbookPath,
111
- write: !options.dryRun,
112
- });
113
- const payload = {
114
- ok: result.ok,
115
- change: changeName,
116
- output: result.outputPath,
117
- playbook: playbookPath,
118
- dry_run: options.dryRun ?? false,
119
- diagnostics: result.diagnostics,
120
- };
121
- if (options.json) {
122
- return { exitCode: result.ok ? 0 : 1, payload };
123
- }
124
- printDiagnostics(result.diagnostics);
125
- if (result.ok) {
126
- if (options.dryRun) {
127
- console.info(`Bundle dry-run OK for "${changeName}" → ${result.outputPath}`);
128
- }
129
- else {
130
- console.info(`Wrote bundle ${result.outputPath}`);
131
- }
132
- return { exitCode: 0, payload };
133
- }
134
- return { exitCode: 1, payload };
135
- }
136
- export async function runApprovalReviewPack(changeName, options) {
137
- validateChangeName(changeName);
138
- const projectRoot = await resolveRootFromCommandOptions({
139
- store: options.store,
140
- cwd: options.cwd,
141
- });
142
- const changeDir = getChangeDirectory(projectRoot, changeName);
143
- const result = await generateReviewPacket({ changeDir });
144
- const payload = {
145
- ok: result.ok,
146
- change: changeName,
147
- packet_path: result.packetPath,
148
- diagnostics: result.diagnostics,
149
- };
150
- if (options.json) {
151
- return { exitCode: result.ok ? 0 : 1, payload };
152
- }
153
- printDiagnostics(result.diagnostics);
154
- if (result.ok) {
155
- console.info(`Wrote review packet ${result.packetPath}`);
156
- console.info('Run AI review (Stage 12e), then: specflow approval review-check');
157
- return { exitCode: 0, payload };
158
- }
159
- return { exitCode: 1, payload };
160
- }
161
- export async function runApprovalReviewCheck(changeName, options) {
162
- validateChangeName(changeName);
163
- const projectRoot = await resolveRootFromCommandOptions({
164
- store: options.store,
165
- cwd: options.cwd,
166
- });
167
- const changeDir = getChangeDirectory(projectRoot, changeName);
168
- const result = await checkApprovalReview({
169
- changeDir,
170
- requirePass: options.requireReview !== false,
171
- });
172
- const payload = {
173
- ok: result.ok,
174
- change: changeName,
175
- verdict: result.review?.verdict,
176
- diagnostics: result.diagnostics,
177
- };
178
- if (options.json) {
179
- return { exitCode: result.ok ? 0 : 1, payload };
180
- }
181
- printDiagnostics(result.diagnostics);
182
- if (result.ok) {
183
- console.info(`Review check passed for "${changeName}" (verdict=${result.review?.verdict ?? 'n/a'}).`);
184
- return { exitCode: 0, payload };
185
- }
186
- return { exitCode: 1, payload };
187
- }
188
- export async function runApprovalReviewHashes(changeName, options) {
189
- validateChangeName(changeName);
190
- const projectRoot = await resolveRootFromCommandOptions({
191
- store: options.store,
192
- cwd: options.cwd,
193
- });
194
- const changeDir = getChangeDirectory(projectRoot, changeName);
195
- let index;
196
- try {
197
- index = await readApprovalIndex(changeDir);
198
- }
199
- catch (error) {
200
- const message = error instanceof Error ? error.message : String(error);
201
- throw new Error(`Cannot read index: ${message}`);
202
- }
203
- const part_hashes = await computeReviewPartHashes(changeDir, index.parts_order);
204
- const payload = { change: changeName, part_hashes };
205
- if (options.json) {
206
- return { exitCode: 0, payload };
207
- }
208
- console.info(JSON.stringify(part_hashes, null, 2));
209
- return { exitCode: 0, payload };
210
- }
211
- export async function runApprovalRender(changeName, options) {
212
- validateChangeName(changeName);
213
- const projectRoot = await resolveRootFromCommandOptions({
214
- store: options.store,
215
- cwd: options.cwd,
216
- });
217
- const changeDir = getChangeDirectory(projectRoot, changeName);
218
- const paths = resolveApprovalRenderPaths(changeDir, options.input, options.output);
219
- const result = await renderApprovalDocument({
220
- changeDir,
221
- inputPath: paths.inputPath,
222
- outputPath: paths.outputPath,
223
- title: options.title,
224
- write: !options.dryRun,
225
- });
226
- const payload = {
227
- ok: result.ok,
228
- change: changeName,
229
- input: paths.inputPath,
230
- output: result.outputPath,
231
- sha256: result.sha256,
232
- dry_run: options.dryRun ?? false,
233
- diagnostics: result.diagnostics,
234
- };
235
- if (options.json) {
236
- return { exitCode: result.ok ? 0 : 1, payload };
237
- }
238
- if (result.diagnostics.length > 0) {
239
- printDiagnostics(result.diagnostics);
240
- }
241
- if (result.ok) {
242
- if (options.dryRun) {
243
- console.info(`Render dry-run OK for "${changeName}" → ${result.outputPath}`);
244
- }
245
- else {
246
- console.info(`Wrote ${result.outputPath}`);
247
- }
248
- return { exitCode: 0, payload };
249
- }
250
- return { exitCode: 1, payload };
251
- }
252
- export async function runApprovalFinalize(changeName, options) {
253
- validateChangeName(changeName);
254
- const projectRoot = await resolveRootFromCommandOptions({
255
- store: options.store,
256
- cwd: options.cwd,
257
- });
258
- const changeDir = getChangeDirectory(projectRoot, changeName);
259
- const bundleMode = options.noBundle ? false : options.bundle ? true : 'auto';
260
- const result = await finalizeApprovalDocument({
261
- changeDir,
262
- workspaceRoot: options.workspaceRoot ?? projectRoot,
263
- strict: options.strict ?? true,
264
- skipReview: options.skipReview ?? false,
265
- force: options.force ?? true,
266
- bundle: bundleMode,
267
- playbook: options.playbook,
268
- dryRun: options.dryRun ?? false,
269
- validateLazy: !options.noLazyCheck,
270
- });
271
- const payload = {
272
- ok: result.ok,
273
- paused: result.paused ?? false,
274
- stage: result.stage,
275
- change: changeName,
276
- output: result.outputPath,
277
- bundle: result.bundlePath,
278
- review_packet: result.reviewPacketPath,
279
- diagnostics: result.diagnostics,
280
- };
281
- if (options.json) {
282
- const exitCode = result.ok ? 0 : result.paused ? 2 : 1;
283
- return { exitCode, payload };
284
- }
285
- printDiagnostics(result.diagnostics);
286
- if (result.paused) {
287
- console.info(`Paused at AI review for "${changeName}". Complete Stage 12e in-session, then re-run finalize.`);
288
- if (result.reviewPacketPath) {
289
- console.info(`Review packet: ${result.reviewPacketPath}`);
290
- }
291
- return { exitCode: 2, payload };
292
- }
293
- if (result.ok) {
294
- console.info(`Finalized approval for "${changeName}" (stage=${result.stage}).`);
295
- if (result.outputPath) {
296
- console.info(`Wrote ${result.outputPath}`);
297
- }
298
- if (result.bundlePath) {
299
- console.info(`Wrote bundle ${result.bundlePath}`);
300
- }
301
- if (options.render && !options.dryRun) {
302
- const paths = resolveApprovalRenderPaths(changeDir, options.renderInput, options.renderOutput);
303
- const renderResult = await renderApprovalDocument({
304
- changeDir,
305
- inputPath: paths.inputPath,
306
- outputPath: paths.outputPath,
307
- write: true,
308
- });
309
- if (renderResult.ok && renderResult.outputPath) {
310
- console.info(`Wrote ${renderResult.outputPath}`);
311
- }
312
- else {
313
- printDiagnostics(renderResult.diagnostics);
314
- return { exitCode: 1, payload: { ...payload, render: renderResult } };
315
- }
316
- }
317
- return { exitCode: 0, payload };
318
- }
319
- console.error(`Finalize failed at stage "${result.stage}" for "${changeName}".`);
320
- return { exitCode: 1, payload };
321
- }
322
- function bindJsonAction(action) {
323
- return async (change, opts) => {
324
- const { exitCode, payload } = await action(change, opts);
325
- if (opts.json) {
326
- console.info(JSON.stringify(payload, null, 2));
327
- }
328
- if (exitCode !== 0) {
329
- process.exitCode = exitCode;
330
- }
331
- };
332
- }
333
- export function registerApprovalAssembleCommand(program) {
334
- const approval = program.command('approval').description('Approval document utilities (ACP)');
335
- addStoreOption(approval
336
- .command('finalize <change>')
337
- .description('Run full ACP finalize: lint → review-check → check → assemble → bundle (preferred entry)')
338
- .option('--skip-review', 'Skip AI review gate even when acp.review is enabled')
339
- .option('--bundle', 'Force multi-repo bundle after assemble')
340
- .option('--no-bundle', 'Skip bundle even when multi_repo.bundle.enabled')
341
- .option('--playbook <path>', 'Path to playbook YAML (bundle step)')
342
- .option('--workspace-root <path>', 'Workspace root for multi-repo bundle')
343
- .option('--force', 'Overwrite existing approval.md (default: true)', true)
344
- .option('--dry-run', 'Validate through check without writing approval.md or bundle')
345
- .option('--strict', 'Strict lint (default: true)', true)
346
- .option('--json', 'Output result as JSON')
347
- .option('--no-lazy-check', 'Skip stub/lazy content validation on parts')
348
- .option('--render', 'Also render approval.md to approval.html after finalize')
349
- .option('--render-input <path>', 'Markdown input for --render (default: approval.md)')
350
- .option('--render-output <path>', 'HTML output for --render (default: approval.html)')
351
- .action(bindJsonAction(runApprovalFinalize)));
352
- addStoreOption(approval
353
- .command('render <change>')
354
- .description('Render approval.md (or bundle markdown) to standalone HTML')
355
- .option('--input <path>', 'Input markdown (default: approval.md)')
356
- .option('--output <path>', 'Output HTML (default: same name with .html)')
357
- .option('--title <text>', 'HTML document title override')
358
- .option('--dry-run', 'Validate render without writing HTML file')
359
- .option('--json', 'Output result as JSON')
360
- .action(bindJsonAction((change, opts) => runApprovalRender(change, opts))));
361
- addStoreOption(approval
362
- .command('assemble <change>')
363
- .description('Merge approval/parts into approval.md (deterministic, no LLM)')
364
- .option('--force', 'Overwrite existing approval.md')
365
- .option('--require-review', 'Require approval/review-result.json pass before assemble')
366
- .option('--json', 'Output result as JSON')
367
- .option('--no-lazy-check', 'Skip stub/lazy content validation on parts')
368
- .action(bindJsonAction((change, opts) => runApprovalAssemble(change, { ...opts, checkOnly: false }))));
369
- addStoreOption(approval
370
- .command('check <change>')
371
- .description('Validate approval parts and index without writing approval.md')
372
- .option('--json', 'Output result as JSON')
373
- .option('--no-lazy-check', 'Skip stub/lazy content validation on parts')
374
- .action(bindJsonAction((change, opts) => runApprovalAssemble(change, { ...opts, checkOnly: true }))));
375
- addStoreOption(approval
376
- .command('lint <change>')
377
- .description('Cross-part consistency lint (IR freeze, pairs_with, page apis)')
378
- .option('--strict', 'Treat inconsistency hints as errors')
379
- .option('--json', 'Output result as JSON')
380
- .action(bindJsonAction(runApprovalLint)));
381
- addStoreOption(approval
382
- .command('bundle <change>')
383
- .description('Deterministic multi-repo bundle view from approval-playbook.yaml')
384
- .option('--playbook <path>', 'Path to playbook YAML')
385
- .option('--workspace-root <path>', 'Workspace root containing multiple repo planning roots')
386
- .option('--dry-run', 'Validate bundle without writing output file')
387
- .option('--json', 'Output result as JSON')
388
- .action(bindJsonAction(runApprovalBundle)));
389
- addStoreOption(approval
390
- .command('review-pack <change>')
391
- .description('Generate AI review packet (review-packet.json) for Stage 12e')
392
- .option('--json', 'Output result as JSON')
393
- .action(bindJsonAction(runApprovalReviewPack)));
394
- addStoreOption(approval
395
- .command('review-hashes <change>')
396
- .description('Print part_hashes[] JSON for review-result.json after Stage 12e fixes')
397
- .option('--json', 'Output result as JSON')
398
- .action(bindJsonAction(runApprovalReviewHashes)));
399
- addStoreOption(approval
400
- .command('review-check <change>')
401
- .description('Validate review-result.json + strict lint before assemble')
402
- .option('--no-require-review', 'Skip requiring review-result when acp.review not enabled')
403
- .option('--json', 'Output result as JSON')
404
- .action(bindJsonAction((change, opts) => runApprovalReviewCheck(change, {
405
- ...opts,
406
- requireReview: opts.noRequireReview ? false : true,
407
- }))));
408
- }
@@ -1,10 +0,0 @@
1
- import type { ApprovalAssembleResult, ApprovalIndex, ApprovalManifest } from './types.js';
2
- export declare function readApprovalIndex(changeDir: string): Promise<ApprovalIndex>;
3
- export declare function readApprovalManifest(changeDir: string): Promise<ApprovalManifest | null>;
4
- export interface AssembleApprovalOptions {
5
- readonly changeDir: string;
6
- readonly checkOnly?: boolean;
7
- readonly force?: boolean;
8
- readonly validateLazy?: boolean;
9
- }
10
- export declare function assembleApprovalDocument(options: AssembleApprovalOptions): Promise<ApprovalAssembleResult>;