@gordon.gan/specflow 1.8.0-beta → 1.8.2-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.
Files changed (181) hide show
  1. package/README.md +7 -5
  2. package/dist/cli/commands/document-run.d.ts +98 -0
  3. package/dist/cli/commands/document-run.js +1064 -0
  4. package/dist/cli/index.js +2 -2
  5. package/dist/core/artifact-language.js +11 -1
  6. package/dist/core/document/asset-paths.d.ts +15 -0
  7. package/dist/core/document/asset-paths.js +38 -0
  8. package/dist/core/document/chapters.d.ts +6 -0
  9. package/dist/core/document/chapters.js +38 -0
  10. package/dist/core/document/coverage.d.ts +25 -0
  11. package/dist/core/document/coverage.js +49 -0
  12. package/dist/core/document/digests.d.ts +40 -0
  13. package/dist/core/document/digests.js +144 -0
  14. package/dist/core/document/engine.d.ts +101 -0
  15. package/dist/core/document/engine.js +722 -0
  16. package/dist/core/document/extract.d.ts +61 -0
  17. package/dist/core/document/extract.js +437 -0
  18. package/dist/core/{approval → document}/forbidden-patterns.d.ts +4 -2
  19. package/dist/core/document/forbidden-patterns.js +36 -0
  20. package/dist/core/document/gates.d.ts +39 -0
  21. package/dist/core/document/gates.js +75 -0
  22. package/dist/core/document/input-digest.d.ts +63 -0
  23. package/dist/core/document/input-digest.js +200 -0
  24. package/dist/core/document/input-features.d.ts +43 -0
  25. package/dist/core/document/input-features.js +161 -0
  26. package/dist/core/document/lint.d.ts +39 -0
  27. package/dist/core/document/lint.js +147 -0
  28. package/dist/core/document/llm.d.ts +28 -0
  29. package/dist/core/document/llm.js +22 -0
  30. package/dist/core/document/map.d.ts +52 -0
  31. package/dist/core/document/map.js +296 -0
  32. package/dist/core/document/outline.d.ts +29 -0
  33. package/dist/core/document/outline.js +171 -0
  34. package/dist/core/document/paths.d.ts +23 -0
  35. package/dist/core/document/paths.js +36 -0
  36. package/dist/core/document/profile-validator.d.ts +23 -0
  37. package/dist/core/document/profile-validator.js +155 -0
  38. package/dist/core/document/profiles.d.ts +23 -0
  39. package/dist/core/document/profiles.js +76 -0
  40. package/dist/core/document/render.d.ts +61 -0
  41. package/dist/core/document/render.js +243 -0
  42. package/dist/core/document/review.d.ts +57 -0
  43. package/dist/core/document/review.js +202 -0
  44. package/dist/core/document/scene-detect.d.ts +32 -0
  45. package/dist/core/document/scene-detect.js +237 -0
  46. package/dist/core/document/schemas.d.ts +1111 -0
  47. package/dist/core/document/schemas.js +191 -0
  48. package/dist/integrations/shared/capability-evidence.js +4 -9
  49. package/dist/integrations/shared/command-catalog.js +2 -1
  50. package/dist/integrations/shared/parity-manifest.js +4 -9
  51. package/package.json +2 -1
  52. package/prompts/apply/phase-a-plan.md +1 -1
  53. package/prompts/document/map/acceptance.md +9 -0
  54. package/prompts/document/map/anti-ai.md +29 -0
  55. package/prompts/document/map/api-design.md +26 -0
  56. package/prompts/document/map/architecture.md +27 -0
  57. package/prompts/document/map/benchmark.md +26 -0
  58. package/prompts/document/map/closed-loop.md +9 -0
  59. package/prompts/document/map/compat-migration.md +31 -0
  60. package/prompts/document/map/component-design.md +30 -0
  61. package/prompts/document/map/config-runtime.md +9 -0
  62. package/prompts/document/map/core-flow.md +62 -0
  63. package/prompts/document/map/core-logic.md +9 -0
  64. package/prompts/document/map/data-model.md +18 -0
  65. package/prompts/document/map/deploy.md +26 -0
  66. package/prompts/document/map/fix.md +8 -0
  67. package/prompts/document/map/frontend-architecture.md +35 -0
  68. package/prompts/document/map/goal.md +8 -0
  69. package/prompts/document/map/impact.md +8 -0
  70. package/prompts/document/map/implementability.md +9 -0
  71. package/prompts/document/map/migration-guide.md +36 -0
  72. package/prompts/document/map/mvp-boundary.md +8 -0
  73. package/prompts/document/map/non-goals.md +8 -0
  74. package/prompts/document/map/ops.md +33 -0
  75. package/prompts/document/map/performance.md +32 -0
  76. package/prompts/document/map/poc-demo.md +25 -0
  77. package/prompts/document/map/regression.md +8 -0
  78. package/prompts/document/map/reproduce.md +8 -0
  79. package/prompts/document/map/requirement.md +8 -0
  80. package/prompts/document/map/research.md +25 -0
  81. package/prompts/document/map/root-cause.md +8 -0
  82. package/prompts/document/map/signoff.md +9 -0
  83. package/prompts/document/map/state-management.md +23 -0
  84. package/prompts/document/map/tech-selection.md +29 -0
  85. package/prompts/document/map/test-strategy.md +24 -0
  86. package/prompts/document/map/ui-design.md +21 -0
  87. package/prompts/document/outline/general.md +46 -0
  88. package/prompts/document/review/ai-review.md +57 -0
  89. package/prompts/document/shared/grounding.md +84 -0
  90. package/prompts/explore/explore-session.md +1 -1
  91. package/prompts/propose/design-draft.md +1 -1
  92. package/prompts/propose/proposal.md +1 -1
  93. package/prompts/propose/specs.md +1 -1
  94. package/prompts/propose/tasks-draft.md +1 -1
  95. package/prompts/refine/design-output.md +1 -1
  96. package/prompts/refine/update-artifacts.md +2 -0
  97. package/prompts/shared/artifact-language.md +13 -0
  98. package/skills/specflow-techdoc/SKILL.md +143 -0
  99. package/skills/specflow-techdoc-synth/SKILL.md +115 -0
  100. package/templates/document/chapters/acceptance.yaml +16 -0
  101. package/templates/document/chapters/api-design.yaml +29 -0
  102. package/templates/document/chapters/architecture.yaml +21 -0
  103. package/templates/document/chapters/benchmark.yaml +20 -0
  104. package/templates/document/chapters/closed-loop.yaml +22 -0
  105. package/templates/document/chapters/compat-migration.yaml +21 -0
  106. package/templates/document/chapters/component-design.yaml +22 -0
  107. package/templates/document/chapters/config-runtime.yaml +16 -0
  108. package/templates/document/chapters/core-flow.yaml +27 -0
  109. package/templates/document/chapters/core-logic.yaml +16 -0
  110. package/templates/document/chapters/data-model.yaml +25 -0
  111. package/templates/document/chapters/deploy.yaml +22 -0
  112. package/templates/document/chapters/fix.yaml +18 -0
  113. package/templates/document/chapters/frontend-architecture.yaml +22 -0
  114. package/templates/document/chapters/goal.yaml +17 -0
  115. package/templates/document/chapters/impact.yaml +16 -0
  116. package/templates/document/chapters/implementability.yaml +22 -0
  117. package/templates/document/chapters/migration-guide.yaml +21 -0
  118. package/templates/document/chapters/mvp-boundary.yaml +20 -0
  119. package/templates/document/chapters/non-goals.yaml +17 -0
  120. package/templates/document/chapters/ops.yaml +25 -0
  121. package/templates/document/chapters/performance.yaml +21 -0
  122. package/templates/document/chapters/poc-demo.yaml +22 -0
  123. package/templates/document/chapters/regression.yaml +16 -0
  124. package/templates/document/chapters/reproduce.yaml +16 -0
  125. package/templates/document/chapters/requirement.yaml +18 -0
  126. package/templates/document/chapters/research.yaml +22 -0
  127. package/templates/document/chapters/root-cause.yaml +18 -0
  128. package/templates/document/chapters/signoff.yaml +15 -0
  129. package/templates/document/chapters/state-management.yaml +22 -0
  130. package/templates/document/chapters/tech-selection.yaml +23 -0
  131. package/templates/document/chapters/test-strategy.yaml +23 -0
  132. package/templates/document/chapters/ui-design.yaml +28 -0
  133. package/templates/document/profiles/0to1.yaml +44 -0
  134. package/templates/document/profiles/approve.yaml +22 -0
  135. package/templates/document/profiles/bugfix.yaml +15 -0
  136. package/templates/document/profiles/feature.yaml +23 -0
  137. package/templates/document/profiles/frontend-0to1.yaml +47 -0
  138. package/templates/document/profiles/migration.yaml +42 -0
  139. package/templates/document/profiles/poc.yaml +46 -0
  140. package/dist/cli/commands/approval-assemble.d.ts +0 -64
  141. package/dist/cli/commands/approval-assemble.js +0 -408
  142. package/dist/core/approval/assemble.d.ts +0 -10
  143. package/dist/core/approval/assemble.js +0 -384
  144. package/dist/core/approval/bundle.d.ts +0 -9
  145. package/dist/core/approval/bundle.js +0 -172
  146. package/dist/core/approval/forbidden-patterns.js +0 -40
  147. package/dist/core/approval/index-schema.d.ts +0 -676
  148. package/dist/core/approval/index-schema.js +0 -193
  149. package/dist/core/approval/index.d.ts +0 -12
  150. package/dist/core/approval/index.js +0 -9
  151. package/dist/core/approval/lint.d.ts +0 -10
  152. package/dist/core/approval/lint.js +0 -302
  153. package/dist/core/approval/paths.d.ts +0 -13
  154. package/dist/core/approval/paths.js +0 -43
  155. package/dist/core/approval/pipeline.d.ts +0 -28
  156. package/dist/core/approval/pipeline.js +0 -146
  157. package/dist/core/approval/playbook-schema.d.ts +0 -182
  158. package/dist/core/approval/playbook-schema.js +0 -51
  159. package/dist/core/approval/render.d.ts +0 -20
  160. package/dist/core/approval/render.js +0 -210
  161. package/dist/core/approval/review-pack.d.ts +0 -26
  162. package/dist/core/approval/review-pack.js +0 -205
  163. package/dist/core/approval/types.d.ts +0 -223
  164. package/dist/core/approval/types.js +0 -1
  165. package/prompts/approval/acp-pipeline.md +0 -106
  166. package/prompts/approval/ai-review.md +0 -145
  167. package/prompts/approval/api-guidance.md +0 -179
  168. package/prompts/approval/database-guidance.md +0 -105
  169. package/prompts/approval/frontend-guidance.md +0 -249
  170. package/prompts/approval/generate.md +0 -1596
  171. package/prompts/approval/multi-repo-guidance.md +0 -263
  172. package/prompts/approval/multi-repo-spoke-subagent.md +0 -94
  173. package/prompts/approval/project-conventions-guidance.md +0 -171
  174. package/prompts/approval/runtime-guidance.md +0 -64
  175. package/prompts/approval/segmented-generation.md +0 -157
  176. package/skills/specflow-approval/SKILL.md +0 -778
  177. package/templates/approval-index.yaml +0 -93
  178. package/templates/approval-part.md +0 -15
  179. package/templates/approval-playbook-talos-scenario-job-compile.yaml +0 -29
  180. package/templates/approval-playbook.yaml +0 -28
  181. package/templates/approval.md +0 -261
@@ -0,0 +1,42 @@
1
+ # 全栈技术升级(F4 · fullstack-migration)
2
+ # 升级评估 → 兼容性分析 → 渐进式迁移 → 迁移指南 → 测试 → 灰度发布/回滚
3
+ id: migration
4
+ name: 技术升级方案
5
+ required:
6
+ - goal # 升级动机/收益/工作量边界
7
+ - impact # Breaking Change 清单/影响范围
8
+ - compat-migration # 兼容性分析 + 渐进式迁移方案 + 新旧 API 对照(扩展)
9
+ - migration-guide # 代码迁移指南(逐模块 checklist/codemod/陷阱/培训)
10
+ - test-strategy # 回归测试(视觉/功能/性能回归)
11
+ - deploy # 灰度发布方案 + 回滚预案
12
+ optional_candidates:
13
+ - performance # 性能基准对比(新旧产物体积/加载/运行时)
14
+ - api-design # API 变化对照表(when: input.containsApiChange)
15
+ - data-model # 数据迁移(when: input.hasPersistence)
16
+ - architecture # 新旧架构对比(后端)
17
+ - ui-design # 样式/UI 迁移范围(when: input.uiInScope)
18
+ - ops # 运维监控(灰度期指标/告警)
19
+ - signoff
20
+ forbidden:
21
+ - reproduce # 非 Bug/功能场景
22
+ - root-cause # (入 shared 闭合,不渲染)
23
+ - fix
24
+ - regression
25
+ - requirement
26
+ - tech-selection # 非 0→1 场景(入 shared 闭合,不渲染)
27
+ - mvp-boundary
28
+ - non-goals
29
+ - research # 非预研(F5 用)
30
+ - poc-demo
31
+ - benchmark
32
+ - frontend-architecture # 迁移不重画前端架构(入 shared 闭合,不渲染)
33
+ - component-design
34
+ - state-management
35
+ - acceptance
36
+ shared:
37
+ - root-cause # 仅闭合允许、不渲染:impact 章节 depends_on [root-cause],但迁移场景不渲染根因章
38
+ - tech-selection # 仅闭合允许、不渲染:architecture(新旧架构对比,可选)依赖
39
+ - frontend-architecture # 仅闭合允许、不渲染:performance 依赖(迁移不重画前端架构)
40
+ appendices:
41
+ - anti-ai
42
+ - handoff
@@ -0,0 +1,46 @@
1
+ # 全栈技术预研/选型(F5 · fullstack-poc)
2
+ # 证据链:research(调研)→ poc-demo(验证)→ benchmark(数据)→ tech-selection(综合推荐)
3
+ # forbidden 已含全部全栈实现章节(frontend-architecture/component-design/state-management/
4
+ # performance/ops/migration-guide/a11y,见 docs/scenario-templates/f5-poc.md §6.4)
5
+ id: poc
6
+ name: 技术预研/选型
7
+ required:
8
+ - research # A 调研:候选 ≥2 + 社区活跃度 + 团队匹配
9
+ - poc-demo # B 验证:最小验证 + 判据 + 已知限制
10
+ - benchmark # B 数据:同条件同设备 + 原始测量值
11
+ - tech-selection # C 综合推荐:评估矩阵 + 一句话立场(声明序渲染在证据链末尾)
12
+ optional_candidates:
13
+ - goal # A 预研背景与边界
14
+ - impact # D 采用后影响范围(dep: root-cause → shared 闭合)
15
+ - signoff # Z 签字栏(when: input.includeSignoff)
16
+ shared:
17
+ - root-cause # 仅闭合允许、不渲染(impact 依赖)
18
+ forbidden:
19
+ - reproduce # 非 Bug
20
+ - root-cause # 非 Bug
21
+ - fix # 非 Bug
22
+ - regression # 非 Bug
23
+ - requirement # 非功能开发
24
+ - api-design # 无实现设计(无契约可冻结)
25
+ - data-model # 无实现设计(无表可建)
26
+ - ui-design # 无实现设计(无页面可画)
27
+ - architecture # 预研不设计架构
28
+ - frontend-architecture # 预研不设计前端架构
29
+ - component-design # 预研不设计组件
30
+ - state-management # 预研不设计状态
31
+ - performance # 预研不写实现性能方案(benchmark 已覆盖测量)
32
+ - ops # 预研不写运维运营
33
+ - migration-guide # 预研不做代码迁移
34
+ - deploy # 无部署交付
35
+ - test-strategy # 无测试策略
36
+ - config-runtime # 无配置实现
37
+ - acceptance # 无验收标准
38
+ - core-logic # 无核心逻辑实现
39
+ - mvp-boundary # 非 0→1
40
+ - non-goals # 非 0→1(一句话立场覆盖)
41
+ - compat-migration # 非迁移(F4)
42
+ - closed-loop # 审批专用
43
+ - implementability # 审批专用
44
+ appendices:
45
+ - anti-ai # 附录 B · 去AI味自检(五维自检表,≥3 条)
46
+ - handoff # 附录 C · 下游交付衔接(交付矩阵 + 三步)
@@ -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>;