@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.
- package/README.md +7 -5
- package/dist/cli/commands/document-run.d.ts +98 -0
- package/dist/cli/commands/document-run.js +1064 -0
- package/dist/cli/index.js +2 -2
- package/dist/core/artifact-language.js +11 -1
- package/dist/core/document/asset-paths.d.ts +15 -0
- package/dist/core/document/asset-paths.js +38 -0
- package/dist/core/document/chapters.d.ts +6 -0
- package/dist/core/document/chapters.js +38 -0
- package/dist/core/document/coverage.d.ts +25 -0
- package/dist/core/document/coverage.js +49 -0
- package/dist/core/document/digests.d.ts +40 -0
- package/dist/core/document/digests.js +144 -0
- package/dist/core/document/engine.d.ts +101 -0
- package/dist/core/document/engine.js +722 -0
- package/dist/core/document/extract.d.ts +61 -0
- package/dist/core/document/extract.js +437 -0
- package/dist/core/{approval → document}/forbidden-patterns.d.ts +4 -2
- package/dist/core/document/forbidden-patterns.js +36 -0
- package/dist/core/document/gates.d.ts +39 -0
- package/dist/core/document/gates.js +75 -0
- package/dist/core/document/input-digest.d.ts +63 -0
- package/dist/core/document/input-digest.js +200 -0
- package/dist/core/document/input-features.d.ts +43 -0
- package/dist/core/document/input-features.js +161 -0
- package/dist/core/document/lint.d.ts +39 -0
- package/dist/core/document/lint.js +147 -0
- package/dist/core/document/llm.d.ts +28 -0
- package/dist/core/document/llm.js +22 -0
- package/dist/core/document/map.d.ts +52 -0
- package/dist/core/document/map.js +296 -0
- package/dist/core/document/outline.d.ts +29 -0
- package/dist/core/document/outline.js +171 -0
- package/dist/core/document/paths.d.ts +23 -0
- package/dist/core/document/paths.js +36 -0
- package/dist/core/document/profile-validator.d.ts +23 -0
- package/dist/core/document/profile-validator.js +155 -0
- package/dist/core/document/profiles.d.ts +23 -0
- package/dist/core/document/profiles.js +76 -0
- package/dist/core/document/render.d.ts +61 -0
- package/dist/core/document/render.js +243 -0
- package/dist/core/document/review.d.ts +57 -0
- package/dist/core/document/review.js +202 -0
- package/dist/core/document/scene-detect.d.ts +32 -0
- package/dist/core/document/scene-detect.js +237 -0
- package/dist/core/document/schemas.d.ts +1111 -0
- package/dist/core/document/schemas.js +191 -0
- package/dist/integrations/shared/capability-evidence.js +4 -9
- package/dist/integrations/shared/command-catalog.js +2 -1
- package/dist/integrations/shared/parity-manifest.js +4 -9
- package/package.json +2 -1
- package/prompts/apply/phase-a-plan.md +1 -1
- package/prompts/document/map/acceptance.md +9 -0
- package/prompts/document/map/anti-ai.md +29 -0
- package/prompts/document/map/api-design.md +26 -0
- package/prompts/document/map/architecture.md +27 -0
- package/prompts/document/map/benchmark.md +26 -0
- package/prompts/document/map/closed-loop.md +9 -0
- package/prompts/document/map/compat-migration.md +31 -0
- package/prompts/document/map/component-design.md +30 -0
- package/prompts/document/map/config-runtime.md +9 -0
- package/prompts/document/map/core-flow.md +62 -0
- package/prompts/document/map/core-logic.md +9 -0
- package/prompts/document/map/data-model.md +18 -0
- package/prompts/document/map/deploy.md +26 -0
- package/prompts/document/map/fix.md +8 -0
- package/prompts/document/map/frontend-architecture.md +35 -0
- package/prompts/document/map/goal.md +8 -0
- package/prompts/document/map/impact.md +8 -0
- package/prompts/document/map/implementability.md +9 -0
- package/prompts/document/map/migration-guide.md +36 -0
- package/prompts/document/map/mvp-boundary.md +8 -0
- package/prompts/document/map/non-goals.md +8 -0
- package/prompts/document/map/ops.md +33 -0
- package/prompts/document/map/performance.md +32 -0
- package/prompts/document/map/poc-demo.md +25 -0
- package/prompts/document/map/regression.md +8 -0
- package/prompts/document/map/reproduce.md +8 -0
- package/prompts/document/map/requirement.md +8 -0
- package/prompts/document/map/research.md +25 -0
- package/prompts/document/map/root-cause.md +8 -0
- package/prompts/document/map/signoff.md +9 -0
- package/prompts/document/map/state-management.md +23 -0
- package/prompts/document/map/tech-selection.md +29 -0
- package/prompts/document/map/test-strategy.md +24 -0
- package/prompts/document/map/ui-design.md +21 -0
- package/prompts/document/outline/general.md +46 -0
- package/prompts/document/review/ai-review.md +57 -0
- package/prompts/document/shared/grounding.md +84 -0
- package/prompts/explore/explore-session.md +1 -1
- package/prompts/propose/design-draft.md +1 -1
- package/prompts/propose/proposal.md +1 -1
- package/prompts/propose/specs.md +1 -1
- package/prompts/propose/tasks-draft.md +1 -1
- package/prompts/refine/design-output.md +1 -1
- package/prompts/refine/update-artifacts.md +2 -0
- package/prompts/shared/artifact-language.md +13 -0
- package/skills/specflow-techdoc/SKILL.md +143 -0
- package/skills/specflow-techdoc-synth/SKILL.md +115 -0
- package/templates/document/chapters/acceptance.yaml +16 -0
- package/templates/document/chapters/api-design.yaml +29 -0
- package/templates/document/chapters/architecture.yaml +21 -0
- package/templates/document/chapters/benchmark.yaml +20 -0
- package/templates/document/chapters/closed-loop.yaml +22 -0
- package/templates/document/chapters/compat-migration.yaml +21 -0
- package/templates/document/chapters/component-design.yaml +22 -0
- package/templates/document/chapters/config-runtime.yaml +16 -0
- package/templates/document/chapters/core-flow.yaml +27 -0
- package/templates/document/chapters/core-logic.yaml +16 -0
- package/templates/document/chapters/data-model.yaml +25 -0
- package/templates/document/chapters/deploy.yaml +22 -0
- package/templates/document/chapters/fix.yaml +18 -0
- package/templates/document/chapters/frontend-architecture.yaml +22 -0
- package/templates/document/chapters/goal.yaml +17 -0
- package/templates/document/chapters/impact.yaml +16 -0
- package/templates/document/chapters/implementability.yaml +22 -0
- package/templates/document/chapters/migration-guide.yaml +21 -0
- package/templates/document/chapters/mvp-boundary.yaml +20 -0
- package/templates/document/chapters/non-goals.yaml +17 -0
- package/templates/document/chapters/ops.yaml +25 -0
- package/templates/document/chapters/performance.yaml +21 -0
- package/templates/document/chapters/poc-demo.yaml +22 -0
- package/templates/document/chapters/regression.yaml +16 -0
- package/templates/document/chapters/reproduce.yaml +16 -0
- package/templates/document/chapters/requirement.yaml +18 -0
- package/templates/document/chapters/research.yaml +22 -0
- package/templates/document/chapters/root-cause.yaml +18 -0
- package/templates/document/chapters/signoff.yaml +15 -0
- package/templates/document/chapters/state-management.yaml +22 -0
- package/templates/document/chapters/tech-selection.yaml +23 -0
- package/templates/document/chapters/test-strategy.yaml +23 -0
- package/templates/document/chapters/ui-design.yaml +28 -0
- package/templates/document/profiles/0to1.yaml +44 -0
- package/templates/document/profiles/approve.yaml +22 -0
- package/templates/document/profiles/bugfix.yaml +15 -0
- package/templates/document/profiles/feature.yaml +23 -0
- package/templates/document/profiles/frontend-0to1.yaml +47 -0
- package/templates/document/profiles/migration.yaml +42 -0
- package/templates/document/profiles/poc.yaml +46 -0
- package/dist/cli/commands/approval-assemble.d.ts +0 -64
- package/dist/cli/commands/approval-assemble.js +0 -408
- package/dist/core/approval/assemble.d.ts +0 -10
- package/dist/core/approval/assemble.js +0 -384
- package/dist/core/approval/bundle.d.ts +0 -9
- package/dist/core/approval/bundle.js +0 -172
- package/dist/core/approval/forbidden-patterns.js +0 -40
- package/dist/core/approval/index-schema.d.ts +0 -676
- package/dist/core/approval/index-schema.js +0 -193
- package/dist/core/approval/index.d.ts +0 -12
- package/dist/core/approval/index.js +0 -9
- package/dist/core/approval/lint.d.ts +0 -10
- package/dist/core/approval/lint.js +0 -302
- package/dist/core/approval/paths.d.ts +0 -13
- package/dist/core/approval/paths.js +0 -43
- package/dist/core/approval/pipeline.d.ts +0 -28
- package/dist/core/approval/pipeline.js +0 -146
- package/dist/core/approval/playbook-schema.d.ts +0 -182
- package/dist/core/approval/playbook-schema.js +0 -51
- package/dist/core/approval/render.d.ts +0 -20
- package/dist/core/approval/render.js +0 -210
- package/dist/core/approval/review-pack.d.ts +0 -26
- package/dist/core/approval/review-pack.js +0 -205
- package/dist/core/approval/types.d.ts +0 -223
- package/dist/core/approval/types.js +0 -1
- package/prompts/approval/acp-pipeline.md +0 -106
- package/prompts/approval/ai-review.md +0 -145
- package/prompts/approval/api-guidance.md +0 -179
- package/prompts/approval/database-guidance.md +0 -105
- package/prompts/approval/frontend-guidance.md +0 -249
- package/prompts/approval/generate.md +0 -1596
- package/prompts/approval/multi-repo-guidance.md +0 -263
- package/prompts/approval/multi-repo-spoke-subagent.md +0 -94
- package/prompts/approval/project-conventions-guidance.md +0 -171
- package/prompts/approval/runtime-guidance.md +0 -64
- package/prompts/approval/segmented-generation.md +0 -157
- package/skills/specflow-approval/SKILL.md +0 -778
- package/templates/approval-index.yaml +0 -93
- package/templates/approval-part.md +0 -15
- package/templates/approval-playbook-talos-scenario-job-compile.yaml +0 -29
- package/templates/approval-playbook.yaml +0 -28
- package/templates/approval.md +0 -261
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { promises as fs } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
import { assembleApprovalDocument, readApprovalIndex } from './assemble.js';
|
|
4
|
-
import { lintApprovalDocument } from './lint.js';
|
|
5
|
-
import { bundleApprovalDocument, resolvePlaybookPath } from './bundle.js';
|
|
6
|
-
import { checkApprovalReview, generateReviewPacket } from './review-pack.js';
|
|
7
|
-
import { getApprovalReviewPath } from './paths.js';
|
|
8
|
-
function mergeDiagnostics(target, next) {
|
|
9
|
-
target.push(...next);
|
|
10
|
-
return next.some((d) => d.severity === 'error');
|
|
11
|
-
}
|
|
12
|
-
async function reviewResultExists(changeDir) {
|
|
13
|
-
try {
|
|
14
|
-
await fs.access(getApprovalReviewPath(changeDir));
|
|
15
|
-
return true;
|
|
16
|
-
}
|
|
17
|
-
catch {
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
function shouldRunBundle(bundleOption, multiRepoBundleEnabled) {
|
|
22
|
-
if (bundleOption === true) {
|
|
23
|
-
return true;
|
|
24
|
-
}
|
|
25
|
-
if (bundleOption === false) {
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
return multiRepoBundleEnabled === true;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* One-shot ACP finalize: lint → review gate → check → assemble → optional bundle.
|
|
32
|
-
* When AI review is required but review-result.json is missing, pauses after review-pack.
|
|
33
|
-
*/
|
|
34
|
-
export async function finalizeApprovalDocument(options) {
|
|
35
|
-
const diagnostics = [];
|
|
36
|
-
const { changeDir, workspaceRoot = join(changeDir, '..', '..', '..'), strict = true, skipReview = false, force = true, bundle = 'auto', dryRun = false, validateLazy = true, } = options;
|
|
37
|
-
let index;
|
|
38
|
-
try {
|
|
39
|
-
index = await readApprovalIndex(changeDir);
|
|
40
|
-
}
|
|
41
|
-
catch (error) {
|
|
42
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
43
|
-
return {
|
|
44
|
-
ok: false,
|
|
45
|
-
stage: 'lint',
|
|
46
|
-
change: '',
|
|
47
|
-
diagnostics: [
|
|
48
|
-
{ code: 'missing_index', severity: 'error', message: `Cannot read index: ${message}` },
|
|
49
|
-
],
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
const lintResult = await lintApprovalDocument({ changeDir, strict });
|
|
53
|
-
if (mergeDiagnostics(diagnostics, lintResult.diagnostics) || !lintResult.ok) {
|
|
54
|
-
return { ok: false, stage: 'lint', change: index.change, diagnostics };
|
|
55
|
-
}
|
|
56
|
-
const reviewEnabled = !skipReview &&
|
|
57
|
-
(index.acp?.review?.enabled === true || index.acp?.review?.required_pass === true);
|
|
58
|
-
let reviewPacketPath;
|
|
59
|
-
if (reviewEnabled) {
|
|
60
|
-
const hasReviewResult = await reviewResultExists(changeDir);
|
|
61
|
-
if (!hasReviewResult) {
|
|
62
|
-
const packResult = await generateReviewPacket({ changeDir });
|
|
63
|
-
reviewPacketPath = packResult.packetPath;
|
|
64
|
-
mergeDiagnostics(diagnostics, packResult.diagnostics);
|
|
65
|
-
if (!packResult.ok) {
|
|
66
|
-
return {
|
|
67
|
-
ok: false,
|
|
68
|
-
stage: 'review-pack',
|
|
69
|
-
change: index.change,
|
|
70
|
-
reviewPacketPath,
|
|
71
|
-
diagnostics,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
diagnostics.push({
|
|
75
|
-
code: 'review_required',
|
|
76
|
-
severity: 'error',
|
|
77
|
-
message: 'AI review required: read prompts/approval/ai-review.md, fix parts, write approval/review-result.json, then re-run specflow approval finalize',
|
|
78
|
-
});
|
|
79
|
-
return {
|
|
80
|
-
ok: false,
|
|
81
|
-
paused: true,
|
|
82
|
-
stage: 'review-required',
|
|
83
|
-
change: index.change,
|
|
84
|
-
reviewPacketPath,
|
|
85
|
-
diagnostics,
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
const reviewCheck = await checkApprovalReview({
|
|
89
|
-
changeDir,
|
|
90
|
-
requirePass: true,
|
|
91
|
-
forceRequire: index.acp?.review?.required_pass === true,
|
|
92
|
-
});
|
|
93
|
-
if (mergeDiagnostics(diagnostics, reviewCheck.diagnostics) || !reviewCheck.ok) {
|
|
94
|
-
return { ok: false, stage: 'review-check', change: index.change, diagnostics };
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
const checkResult = await assembleApprovalDocument({
|
|
98
|
-
changeDir,
|
|
99
|
-
checkOnly: true,
|
|
100
|
-
validateLazy,
|
|
101
|
-
});
|
|
102
|
-
if (mergeDiagnostics(diagnostics, checkResult.diagnostics) || !checkResult.ok) {
|
|
103
|
-
return { ok: false, stage: 'check', change: index.change, diagnostics };
|
|
104
|
-
}
|
|
105
|
-
if (dryRun) {
|
|
106
|
-
return { ok: true, stage: 'done', change: index.change, diagnostics };
|
|
107
|
-
}
|
|
108
|
-
const assembleResult = await assembleApprovalDocument({
|
|
109
|
-
changeDir,
|
|
110
|
-
checkOnly: false,
|
|
111
|
-
force,
|
|
112
|
-
validateLazy,
|
|
113
|
-
});
|
|
114
|
-
if (mergeDiagnostics(diagnostics, assembleResult.diagnostics) || !assembleResult.ok) {
|
|
115
|
-
return { ok: false, stage: 'assemble', change: index.change, diagnostics };
|
|
116
|
-
}
|
|
117
|
-
let bundlePath;
|
|
118
|
-
if (shouldRunBundle(bundle, index.multi_repo?.bundle?.enabled)) {
|
|
119
|
-
const playbookPath = await resolvePlaybookPath(changeDir, options.playbook);
|
|
120
|
-
const bundleResult = await bundleApprovalDocument({
|
|
121
|
-
workspaceRoot,
|
|
122
|
-
changeDir,
|
|
123
|
-
playbookPath,
|
|
124
|
-
write: true,
|
|
125
|
-
});
|
|
126
|
-
bundlePath = bundleResult.outputPath;
|
|
127
|
-
if (mergeDiagnostics(diagnostics, bundleResult.diagnostics) || !bundleResult.ok) {
|
|
128
|
-
return {
|
|
129
|
-
ok: false,
|
|
130
|
-
stage: 'bundle',
|
|
131
|
-
change: index.change,
|
|
132
|
-
outputPath: assembleResult.outputPath,
|
|
133
|
-
bundlePath,
|
|
134
|
-
diagnostics,
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
return {
|
|
139
|
-
ok: true,
|
|
140
|
-
stage: 'done',
|
|
141
|
-
change: index.change,
|
|
142
|
-
outputPath: assembleResult.outputPath,
|
|
143
|
-
bundlePath,
|
|
144
|
-
diagnostics,
|
|
145
|
-
};
|
|
146
|
-
}
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import type { ApprovalPlaybook } from './types.js';
|
|
3
|
-
export declare const approvalPlaybookSchema: z.ZodObject<{
|
|
4
|
-
schema: z.ZodLiteral<"specflow.approval.playbook/v1">;
|
|
5
|
-
change: z.ZodString;
|
|
6
|
-
title: z.ZodString;
|
|
7
|
-
primary_repo: z.ZodString;
|
|
8
|
-
sources: z.ZodArray<z.ZodObject<{
|
|
9
|
-
repo: z.ZodString;
|
|
10
|
-
change: z.ZodString;
|
|
11
|
-
include_parts: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"all">, z.ZodArray<z.ZodString, "many">]>>;
|
|
12
|
-
root: z.ZodOptional<z.ZodString>;
|
|
13
|
-
}, "strip", z.ZodTypeAny, {
|
|
14
|
-
repo: string;
|
|
15
|
-
change: string;
|
|
16
|
-
root?: string | undefined;
|
|
17
|
-
include_parts?: string[] | "all" | undefined;
|
|
18
|
-
}, {
|
|
19
|
-
repo: string;
|
|
20
|
-
change: string;
|
|
21
|
-
root?: string | undefined;
|
|
22
|
-
include_parts?: string[] | "all" | undefined;
|
|
23
|
-
}>, "many">;
|
|
24
|
-
overlay: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
25
|
-
assemble: z.ZodObject<{
|
|
26
|
-
inject_headers: z.ZodOptional<z.ZodBoolean>;
|
|
27
|
-
dedupe_appendix: z.ZodOptional<z.ZodBoolean>;
|
|
28
|
-
output: z.ZodString;
|
|
29
|
-
readonly: z.ZodOptional<z.ZodBoolean>;
|
|
30
|
-
}, "strip", z.ZodTypeAny, {
|
|
31
|
-
output: string;
|
|
32
|
-
readonly?: boolean | undefined;
|
|
33
|
-
inject_headers?: boolean | undefined;
|
|
34
|
-
dedupe_appendix?: boolean | undefined;
|
|
35
|
-
}, {
|
|
36
|
-
output: string;
|
|
37
|
-
readonly?: boolean | undefined;
|
|
38
|
-
inject_headers?: boolean | undefined;
|
|
39
|
-
dedupe_appendix?: boolean | undefined;
|
|
40
|
-
}>;
|
|
41
|
-
}, "strip", z.ZodTypeAny, {
|
|
42
|
-
schema: "specflow.approval.playbook/v1";
|
|
43
|
-
change: string;
|
|
44
|
-
primary_repo: string;
|
|
45
|
-
title: string;
|
|
46
|
-
sources: {
|
|
47
|
-
repo: string;
|
|
48
|
-
change: string;
|
|
49
|
-
root?: string | undefined;
|
|
50
|
-
include_parts?: string[] | "all" | undefined;
|
|
51
|
-
}[];
|
|
52
|
-
assemble: {
|
|
53
|
-
output: string;
|
|
54
|
-
readonly?: boolean | undefined;
|
|
55
|
-
inject_headers?: boolean | undefined;
|
|
56
|
-
dedupe_appendix?: boolean | undefined;
|
|
57
|
-
};
|
|
58
|
-
overlay?: string[] | undefined;
|
|
59
|
-
}, {
|
|
60
|
-
schema: "specflow.approval.playbook/v1";
|
|
61
|
-
change: string;
|
|
62
|
-
primary_repo: string;
|
|
63
|
-
title: string;
|
|
64
|
-
sources: {
|
|
65
|
-
repo: string;
|
|
66
|
-
change: string;
|
|
67
|
-
root?: string | undefined;
|
|
68
|
-
include_parts?: string[] | "all" | undefined;
|
|
69
|
-
}[];
|
|
70
|
-
assemble: {
|
|
71
|
-
output: string;
|
|
72
|
-
readonly?: boolean | undefined;
|
|
73
|
-
inject_headers?: boolean | undefined;
|
|
74
|
-
dedupe_appendix?: boolean | undefined;
|
|
75
|
-
};
|
|
76
|
-
overlay?: string[] | undefined;
|
|
77
|
-
}>;
|
|
78
|
-
export declare const approvalReviewResultSchema: z.ZodObject<{
|
|
79
|
-
schema: z.ZodLiteral<"specflow.approval.review/v1">;
|
|
80
|
-
change: z.ZodString;
|
|
81
|
-
reviewed_at: z.ZodString;
|
|
82
|
-
reviewer: z.ZodEnum<["ai", "human"]>;
|
|
83
|
-
findings: z.ZodArray<z.ZodObject<{
|
|
84
|
-
id: z.ZodString;
|
|
85
|
-
severity: z.ZodEnum<["critical", "important", "minor"]>;
|
|
86
|
-
category: z.ZodString;
|
|
87
|
-
message: z.ZodString;
|
|
88
|
-
part: z.ZodOptional<z.ZodString>;
|
|
89
|
-
status: z.ZodEnum<["open", "fixed", "waived"]>;
|
|
90
|
-
waiver_reason: z.ZodOptional<z.ZodString>;
|
|
91
|
-
}, "strip", z.ZodTypeAny, {
|
|
92
|
-
status: "fixed" | "open" | "waived";
|
|
93
|
-
message: string;
|
|
94
|
-
id: string;
|
|
95
|
-
severity: "critical" | "important" | "minor";
|
|
96
|
-
category: string;
|
|
97
|
-
part?: string | undefined;
|
|
98
|
-
waiver_reason?: string | undefined;
|
|
99
|
-
}, {
|
|
100
|
-
status: "fixed" | "open" | "waived";
|
|
101
|
-
message: string;
|
|
102
|
-
id: string;
|
|
103
|
-
severity: "critical" | "important" | "minor";
|
|
104
|
-
category: string;
|
|
105
|
-
part?: string | undefined;
|
|
106
|
-
waiver_reason?: string | undefined;
|
|
107
|
-
}>, "many">;
|
|
108
|
-
verdict: z.ZodEnum<["pass", "fail", "pass_with_waivers"]>;
|
|
109
|
-
summary: z.ZodOptional<z.ZodString>;
|
|
110
|
-
part_hashes: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
111
|
-
id: z.ZodString;
|
|
112
|
-
sha256: z.ZodString;
|
|
113
|
-
}, "strip", z.ZodTypeAny, {
|
|
114
|
-
id: string;
|
|
115
|
-
sha256: string;
|
|
116
|
-
}, {
|
|
117
|
-
id: string;
|
|
118
|
-
sha256: string;
|
|
119
|
-
}>, "many">>;
|
|
120
|
-
}, "strip", z.ZodTypeAny, {
|
|
121
|
-
schema: "specflow.approval.review/v1";
|
|
122
|
-
change: string;
|
|
123
|
-
reviewed_at: string;
|
|
124
|
-
reviewer: "ai" | "human";
|
|
125
|
-
findings: {
|
|
126
|
-
status: "fixed" | "open" | "waived";
|
|
127
|
-
message: string;
|
|
128
|
-
id: string;
|
|
129
|
-
severity: "critical" | "important" | "minor";
|
|
130
|
-
category: string;
|
|
131
|
-
part?: string | undefined;
|
|
132
|
-
waiver_reason?: string | undefined;
|
|
133
|
-
}[];
|
|
134
|
-
verdict: "fail" | "pass" | "pass_with_waivers";
|
|
135
|
-
part_hashes?: {
|
|
136
|
-
id: string;
|
|
137
|
-
sha256: string;
|
|
138
|
-
}[] | undefined;
|
|
139
|
-
summary?: string | undefined;
|
|
140
|
-
}, {
|
|
141
|
-
schema: "specflow.approval.review/v1";
|
|
142
|
-
change: string;
|
|
143
|
-
reviewed_at: string;
|
|
144
|
-
reviewer: "ai" | "human";
|
|
145
|
-
findings: {
|
|
146
|
-
status: "fixed" | "open" | "waived";
|
|
147
|
-
message: string;
|
|
148
|
-
id: string;
|
|
149
|
-
severity: "critical" | "important" | "minor";
|
|
150
|
-
category: string;
|
|
151
|
-
part?: string | undefined;
|
|
152
|
-
waiver_reason?: string | undefined;
|
|
153
|
-
}[];
|
|
154
|
-
verdict: "fail" | "pass" | "pass_with_waivers";
|
|
155
|
-
part_hashes?: {
|
|
156
|
-
id: string;
|
|
157
|
-
sha256: string;
|
|
158
|
-
}[] | undefined;
|
|
159
|
-
summary?: string | undefined;
|
|
160
|
-
}>;
|
|
161
|
-
export declare function parseApprovalPlaybook(raw: unknown): ApprovalPlaybook;
|
|
162
|
-
export declare function parseApprovalReviewResult(raw: unknown): {
|
|
163
|
-
schema: "specflow.approval.review/v1";
|
|
164
|
-
change: string;
|
|
165
|
-
reviewed_at: string;
|
|
166
|
-
reviewer: "ai" | "human";
|
|
167
|
-
findings: {
|
|
168
|
-
status: "fixed" | "open" | "waived";
|
|
169
|
-
message: string;
|
|
170
|
-
id: string;
|
|
171
|
-
severity: "critical" | "important" | "minor";
|
|
172
|
-
category: string;
|
|
173
|
-
part?: string | undefined;
|
|
174
|
-
waiver_reason?: string | undefined;
|
|
175
|
-
}[];
|
|
176
|
-
verdict: "fail" | "pass" | "pass_with_waivers";
|
|
177
|
-
part_hashes?: {
|
|
178
|
-
id: string;
|
|
179
|
-
sha256: string;
|
|
180
|
-
}[] | undefined;
|
|
181
|
-
summary?: string | undefined;
|
|
182
|
-
};
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
export const approvalPlaybookSchema = z.object({
|
|
3
|
-
schema: z.literal('specflow.approval.playbook/v1'),
|
|
4
|
-
change: z.string().min(1),
|
|
5
|
-
title: z.string().min(1),
|
|
6
|
-
primary_repo: z.string().min(1),
|
|
7
|
-
sources: z
|
|
8
|
-
.array(z.object({
|
|
9
|
-
repo: z.string().min(1),
|
|
10
|
-
change: z.string().min(1),
|
|
11
|
-
include_parts: z.union([z.literal('all'), z.array(z.string().min(1))]).optional(),
|
|
12
|
-
root: z.string().min(1).optional(),
|
|
13
|
-
}))
|
|
14
|
-
.min(1),
|
|
15
|
-
overlay: z.array(z.string().min(1)).optional(),
|
|
16
|
-
assemble: z.object({
|
|
17
|
-
inject_headers: z.boolean().optional(),
|
|
18
|
-
dedupe_appendix: z.boolean().optional(),
|
|
19
|
-
output: z.string().min(1),
|
|
20
|
-
readonly: z.boolean().optional(),
|
|
21
|
-
}),
|
|
22
|
-
});
|
|
23
|
-
export const approvalReviewResultSchema = z.object({
|
|
24
|
-
schema: z.literal('specflow.approval.review/v1'),
|
|
25
|
-
change: z.string().min(1),
|
|
26
|
-
reviewed_at: z.string().min(1),
|
|
27
|
-
reviewer: z.enum(['ai', 'human']),
|
|
28
|
-
findings: z.array(z.object({
|
|
29
|
-
id: z.string().min(1),
|
|
30
|
-
severity: z.enum(['critical', 'important', 'minor']),
|
|
31
|
-
category: z.string().min(1),
|
|
32
|
-
message: z.string().min(1),
|
|
33
|
-
part: z.string().optional(),
|
|
34
|
-
status: z.enum(['open', 'fixed', 'waived']),
|
|
35
|
-
waiver_reason: z.string().optional(),
|
|
36
|
-
})),
|
|
37
|
-
verdict: z.enum(['pass', 'fail', 'pass_with_waivers']),
|
|
38
|
-
summary: z.string().optional(),
|
|
39
|
-
part_hashes: z
|
|
40
|
-
.array(z.object({
|
|
41
|
-
id: z.string().min(1),
|
|
42
|
-
sha256: z.string().min(64).max(64),
|
|
43
|
-
}))
|
|
44
|
-
.optional(),
|
|
45
|
-
});
|
|
46
|
-
export function parseApprovalPlaybook(raw) {
|
|
47
|
-
return approvalPlaybookSchema.parse(raw);
|
|
48
|
-
}
|
|
49
|
-
export function parseApprovalReviewResult(raw) {
|
|
50
|
-
return approvalReviewResultSchema.parse(raw);
|
|
51
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { ApprovalDiagnostic } from './types.js';
|
|
2
|
-
export interface RenderApprovalOptions {
|
|
3
|
-
readonly changeDir: string;
|
|
4
|
-
readonly inputPath?: string;
|
|
5
|
-
readonly outputPath?: string;
|
|
6
|
-
readonly title?: string;
|
|
7
|
-
readonly write?: boolean;
|
|
8
|
-
}
|
|
9
|
-
export interface RenderApprovalResult {
|
|
10
|
-
readonly ok: boolean;
|
|
11
|
-
readonly outputPath?: string;
|
|
12
|
-
readonly html?: string;
|
|
13
|
-
readonly sha256?: string;
|
|
14
|
-
readonly diagnostics: readonly ApprovalDiagnostic[];
|
|
15
|
-
}
|
|
16
|
-
export declare function renderApprovalDocument(options: RenderApprovalOptions): Promise<RenderApprovalResult>;
|
|
17
|
-
export declare function resolveApprovalRenderPaths(changeDir: string, input?: string, output?: string): {
|
|
18
|
-
inputPath: string;
|
|
19
|
-
outputPath: string;
|
|
20
|
-
};
|
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
import { promises as fs } from 'node:fs';
|
|
3
|
-
import { dirname, join } from 'node:path';
|
|
4
|
-
import { marked } from 'marked';
|
|
5
|
-
import { getApprovalOutputPath } from './paths.js';
|
|
6
|
-
const APPROVAL_HTML_STYLES = `
|
|
7
|
-
:root {
|
|
8
|
-
color-scheme: light dark;
|
|
9
|
-
--text: #1a1a1a;
|
|
10
|
-
--muted: #555;
|
|
11
|
-
--border: #ddd;
|
|
12
|
-
--code-bg: #f6f8fa;
|
|
13
|
-
--quote-bg: #f0f4ff;
|
|
14
|
-
}
|
|
15
|
-
@media (prefers-color-scheme: dark) {
|
|
16
|
-
:root {
|
|
17
|
-
--text: #e6edf3;
|
|
18
|
-
--muted: #9da7b3;
|
|
19
|
-
--border: #30363d;
|
|
20
|
-
--code-bg: #161b22;
|
|
21
|
-
--quote-bg: #1c2333;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
* { box-sizing: border-box; }
|
|
25
|
-
body {
|
|
26
|
-
margin: 0;
|
|
27
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC",
|
|
28
|
-
"Hiragino Sans GB", "Microsoft YaHei", sans-serif;
|
|
29
|
-
line-height: 1.65;
|
|
30
|
-
color: var(--text);
|
|
31
|
-
background: #fff;
|
|
32
|
-
}
|
|
33
|
-
@media (prefers-color-scheme: dark) {
|
|
34
|
-
body { background: #0d1117; }
|
|
35
|
-
}
|
|
36
|
-
.page {
|
|
37
|
-
max-width: 920px;
|
|
38
|
-
margin: 0 auto;
|
|
39
|
-
padding: 2rem 1.5rem 4rem;
|
|
40
|
-
}
|
|
41
|
-
.meta {
|
|
42
|
-
font-size: 0.875rem;
|
|
43
|
-
color: var(--muted);
|
|
44
|
-
margin-bottom: 2rem;
|
|
45
|
-
padding-bottom: 1rem;
|
|
46
|
-
border-bottom: 1px solid var(--border);
|
|
47
|
-
}
|
|
48
|
-
article h1 { font-size: 1.75rem; margin-top: 0; }
|
|
49
|
-
article h2 { font-size: 1.35rem; margin-top: 2rem; border-bottom: 1px solid var(--border); padding-bottom: 0.35rem; }
|
|
50
|
-
article h3 { font-size: 1.15rem; margin-top: 1.5rem; }
|
|
51
|
-
article h4, article h5 { font-size: 1rem; margin-top: 1.25rem; }
|
|
52
|
-
article p, article li { margin: 0.5rem 0; }
|
|
53
|
-
article blockquote {
|
|
54
|
-
margin: 1rem 0;
|
|
55
|
-
padding: 0.75rem 1rem;
|
|
56
|
-
border-left: 4px solid #0969da;
|
|
57
|
-
background: var(--quote-bg);
|
|
58
|
-
color: var(--muted);
|
|
59
|
-
}
|
|
60
|
-
article table {
|
|
61
|
-
width: 100%;
|
|
62
|
-
border-collapse: collapse;
|
|
63
|
-
margin: 1rem 0;
|
|
64
|
-
font-size: 0.92rem;
|
|
65
|
-
}
|
|
66
|
-
article th, article td {
|
|
67
|
-
border: 1px solid var(--border);
|
|
68
|
-
padding: 0.45rem 0.65rem;
|
|
69
|
-
text-align: left;
|
|
70
|
-
}
|
|
71
|
-
article th { background: var(--code-bg); }
|
|
72
|
-
article code {
|
|
73
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
74
|
-
font-size: 0.9em;
|
|
75
|
-
background: var(--code-bg);
|
|
76
|
-
padding: 0.15em 0.35em;
|
|
77
|
-
border-radius: 4px;
|
|
78
|
-
}
|
|
79
|
-
article pre {
|
|
80
|
-
overflow-x: auto;
|
|
81
|
-
padding: 1rem;
|
|
82
|
-
background: var(--code-bg);
|
|
83
|
-
border: 1px solid var(--border);
|
|
84
|
-
border-radius: 6px;
|
|
85
|
-
}
|
|
86
|
-
article pre code { background: none; padding: 0; }
|
|
87
|
-
article hr { border: none; border-top: 1px solid var(--border); margin: 2rem 0; }
|
|
88
|
-
.mermaid {
|
|
89
|
-
margin: 1rem 0;
|
|
90
|
-
text-align: center;
|
|
91
|
-
}
|
|
92
|
-
`.trim();
|
|
93
|
-
function sha256(content) {
|
|
94
|
-
return createHash('sha256').update(content, 'utf8').digest('hex');
|
|
95
|
-
}
|
|
96
|
-
function defaultHtmlPath(markdownPath) {
|
|
97
|
-
if (markdownPath.endsWith('.md')) {
|
|
98
|
-
return markdownPath.slice(0, -3) + '.html';
|
|
99
|
-
}
|
|
100
|
-
return `${markdownPath}.html`;
|
|
101
|
-
}
|
|
102
|
-
function extractTitle(markdown, fallback) {
|
|
103
|
-
const match = markdown.match(/^#\s+(.+)$/m);
|
|
104
|
-
return match?.[1]?.trim() ?? fallback;
|
|
105
|
-
}
|
|
106
|
-
function configureMarked() {
|
|
107
|
-
marked.use({
|
|
108
|
-
gfm: true,
|
|
109
|
-
breaks: false,
|
|
110
|
-
});
|
|
111
|
-
marked.use({
|
|
112
|
-
renderer: {
|
|
113
|
-
code({ text, lang }) {
|
|
114
|
-
if (lang === 'mermaid') {
|
|
115
|
-
return `<pre class="mermaid">${escapeHtml(text)}</pre>`;
|
|
116
|
-
}
|
|
117
|
-
const language = lang ? ` class="language-${lang}"` : '';
|
|
118
|
-
return `<pre><code${language}>${escapeHtml(text)}</code></pre>`;
|
|
119
|
-
},
|
|
120
|
-
},
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
function escapeHtml(value) {
|
|
124
|
-
return value
|
|
125
|
-
.replace(/&/g, '&')
|
|
126
|
-
.replace(/</g, '<')
|
|
127
|
-
.replace(/>/g, '>')
|
|
128
|
-
.replace(/"/g, '"');
|
|
129
|
-
}
|
|
130
|
-
function buildHtmlDocument(title, bodyHtml, sourcePath) {
|
|
131
|
-
const hasMermaid = bodyHtml.includes('class="mermaid"');
|
|
132
|
-
const mermaidScript = hasMermaid
|
|
133
|
-
? `<script type="module">
|
|
134
|
-
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
|
|
135
|
-
mermaid.initialize({ startOnLoad: true, theme: 'neutral' });
|
|
136
|
-
</script>`
|
|
137
|
-
: '';
|
|
138
|
-
return `<!DOCTYPE html>
|
|
139
|
-
<html lang="zh-CN">
|
|
140
|
-
<head>
|
|
141
|
-
<meta charset="utf-8" />
|
|
142
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
143
|
-
<title>${escapeHtml(title)}</title>
|
|
144
|
-
<style>${APPROVAL_HTML_STYLES}</style>
|
|
145
|
-
</head>
|
|
146
|
-
<body>
|
|
147
|
-
<div class="page">
|
|
148
|
-
<div class="meta">SpecFlow approval render · source: ${escapeHtml(sourcePath)} · generated by <code>specflow approval render</code></div>
|
|
149
|
-
<article>${bodyHtml}</article>
|
|
150
|
-
</div>
|
|
151
|
-
${mermaidScript}
|
|
152
|
-
</body>
|
|
153
|
-
</html>
|
|
154
|
-
`;
|
|
155
|
-
}
|
|
156
|
-
export async function renderApprovalDocument(options) {
|
|
157
|
-
const diagnostics = [];
|
|
158
|
-
const { changeDir, write = true } = options;
|
|
159
|
-
const inputPath = options.inputPath ?? getApprovalOutputPath(changeDir);
|
|
160
|
-
const outputPath = options.outputPath ?? defaultHtmlPath(inputPath);
|
|
161
|
-
let markdown;
|
|
162
|
-
try {
|
|
163
|
-
markdown = await fs.readFile(inputPath, 'utf-8');
|
|
164
|
-
}
|
|
165
|
-
catch (error) {
|
|
166
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
167
|
-
return {
|
|
168
|
-
ok: false,
|
|
169
|
-
diagnostics: [
|
|
170
|
-
{
|
|
171
|
-
code: 'render_input_missing',
|
|
172
|
-
severity: 'error',
|
|
173
|
-
message: `Cannot read markdown: ${inputPath} (${message})`,
|
|
174
|
-
},
|
|
175
|
-
],
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
if (markdown.trim().length < 20) {
|
|
179
|
-
diagnostics.push({
|
|
180
|
-
code: 'render_input_empty',
|
|
181
|
-
severity: 'error',
|
|
182
|
-
message: `Markdown input too short or empty: ${inputPath}`,
|
|
183
|
-
});
|
|
184
|
-
return { ok: false, diagnostics };
|
|
185
|
-
}
|
|
186
|
-
configureMarked();
|
|
187
|
-
const bodyHtml = await marked.parse(markdown);
|
|
188
|
-
const title = options.title ?? extractTitle(markdown, 'Approval Document');
|
|
189
|
-
const html = buildHtmlDocument(title, bodyHtml, inputPath);
|
|
190
|
-
const hash = sha256(html);
|
|
191
|
-
if (!write) {
|
|
192
|
-
return { ok: true, outputPath, html, sha256: hash, diagnostics };
|
|
193
|
-
}
|
|
194
|
-
await fs.mkdir(dirname(outputPath), { recursive: true });
|
|
195
|
-
await fs.writeFile(outputPath, html, 'utf-8');
|
|
196
|
-
return { ok: true, outputPath, html, sha256: hash, diagnostics };
|
|
197
|
-
}
|
|
198
|
-
export function resolveApprovalRenderPaths(changeDir, input, output) {
|
|
199
|
-
const inputPath = input
|
|
200
|
-
? input.startsWith('/')
|
|
201
|
-
? input
|
|
202
|
-
: join(changeDir, input)
|
|
203
|
-
: getApprovalOutputPath(changeDir);
|
|
204
|
-
const outputPath = output
|
|
205
|
-
? output.startsWith('/')
|
|
206
|
-
? output
|
|
207
|
-
: join(changeDir, output)
|
|
208
|
-
: defaultHtmlPath(inputPath);
|
|
209
|
-
return { inputPath, outputPath };
|
|
210
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { ApprovalDiagnostic, ApprovalReviewPacket, ApprovalReviewResult } from './types.js';
|
|
2
|
-
export declare function computeReviewPartHashes(changeDir: string, partIds: readonly string[]): Promise<{
|
|
3
|
-
id: string;
|
|
4
|
-
sha256: string;
|
|
5
|
-
}[]>;
|
|
6
|
-
export interface ReviewPackOptions {
|
|
7
|
-
readonly changeDir: string;
|
|
8
|
-
}
|
|
9
|
-
export interface ReviewPackResult {
|
|
10
|
-
readonly ok: boolean;
|
|
11
|
-
readonly packet?: ApprovalReviewPacket;
|
|
12
|
-
readonly packetPath?: string;
|
|
13
|
-
readonly diagnostics: readonly ApprovalDiagnostic[];
|
|
14
|
-
}
|
|
15
|
-
export declare function generateReviewPacket(options: ReviewPackOptions): Promise<ReviewPackResult>;
|
|
16
|
-
export interface ReviewCheckOptions {
|
|
17
|
-
readonly changeDir: string;
|
|
18
|
-
readonly requirePass?: boolean;
|
|
19
|
-
readonly forceRequire?: boolean;
|
|
20
|
-
}
|
|
21
|
-
export interface ReviewCheckResult {
|
|
22
|
-
readonly ok: boolean;
|
|
23
|
-
readonly review?: ApprovalReviewResult;
|
|
24
|
-
readonly diagnostics: readonly ApprovalDiagnostic[];
|
|
25
|
-
}
|
|
26
|
-
export declare function checkApprovalReview(options: ReviewCheckOptions): Promise<ReviewCheckResult>;
|