qfai 0.7.2 → 0.8.0
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/LICENSE +21 -0
- package/README.md +1 -1
- package/assets/init/.qfai/README.md +4 -1
- package/assets/init/.qfai/promptpack/steering/compatibility-vs-change.md +34 -0
- package/assets/init/.qfai/prompts.local/README.md +5 -0
- package/dist/cli/index.cjs +122 -51
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.mjs +122 -51
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.cjs +109 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +109 -45
- package/dist/index.mjs.map +1 -1
- package/package.json +13 -2
package/dist/index.mjs
CHANGED
|
@@ -1179,8 +1179,8 @@ import { readFile as readFile4 } from "fs/promises";
|
|
|
1179
1179
|
import path7 from "path";
|
|
1180
1180
|
import { fileURLToPath } from "url";
|
|
1181
1181
|
async function resolveToolVersion() {
|
|
1182
|
-
if ("0.
|
|
1183
|
-
return "0.
|
|
1182
|
+
if ("0.8.0".length > 0) {
|
|
1183
|
+
return "0.8.0";
|
|
1184
1184
|
}
|
|
1185
1185
|
try {
|
|
1186
1186
|
const packagePath = resolvePackageJsonPath();
|
|
@@ -1551,7 +1551,7 @@ async function validateDeltas(root, config) {
|
|
|
1551
1551
|
issues.push(
|
|
1552
1552
|
issue2(
|
|
1553
1553
|
"QFAI-DELTA-002",
|
|
1554
|
-
"delta.md \u306E\u5909\u66F4\u533A\u5206\u304C\u4E0D\u8DB3\u3057\u3066\u3044\u307E\u3059\u3002",
|
|
1554
|
+
"delta.md \u306E\u5909\u66F4\u533A\u5206\u304C\u4E0D\u8DB3\u3057\u3066\u3044\u307E\u3059\u3002`## \u5909\u66F4\u533A\u5206` \u3068\u30C1\u30A7\u30C3\u30AF\u30DC\u30C3\u30AF\u30B9\uFF08Compatibility / Change/Improvement\uFF09\u3092\u8FFD\u52A0\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
1555
1555
|
"error",
|
|
1556
1556
|
deltaPath,
|
|
1557
1557
|
"delta.section"
|
|
@@ -2094,7 +2094,7 @@ async function validateTraceability(root, config) {
|
|
|
2094
2094
|
issues.push(
|
|
2095
2095
|
issue6(
|
|
2096
2096
|
"QFAI-TRACE-020",
|
|
2097
|
-
"Spec \u306B QFAI-CONTRACT-REF \u304C\u3042\u308A\u307E\u305B\u3093\u3002",
|
|
2097
|
+
"Spec \u306B QFAI-CONTRACT-REF \u304C\u3042\u308A\u307E\u305B\u3093\u3002\u4F8B: `QFAI-CONTRACT-REF: none` \u307E\u305F\u306F `QFAI-CONTRACT-REF: UI-0001`",
|
|
2098
2098
|
"error",
|
|
2099
2099
|
file,
|
|
2100
2100
|
"traceability.specContractRefRequired"
|
|
@@ -2105,7 +2105,7 @@ async function validateTraceability(root, config) {
|
|
|
2105
2105
|
issues.push(
|
|
2106
2106
|
issue6(
|
|
2107
2107
|
"QFAI-TRACE-023",
|
|
2108
|
-
"Spec \u306E QFAI-CONTRACT-REF \u306B none \u3068\u5951\u7D04 ID \u304C\u6DF7\u5728\u3057\u3066\u3044\u307E\u3059\u3002",
|
|
2108
|
+
"Spec \u306E QFAI-CONTRACT-REF \u306B none \u3068\u5951\u7D04 ID \u304C\u6DF7\u5728\u3057\u3066\u3044\u307E\u3059\u3002none \u304B \u5951\u7D04ID \u306E\u3069\u3061\u3089\u304B\u4E00\u65B9\u3060\u3051\u306B\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
2109
2109
|
"error",
|
|
2110
2110
|
file,
|
|
2111
2111
|
"traceability.specContractRefFormat"
|
|
@@ -2118,7 +2118,7 @@ async function validateTraceability(root, config) {
|
|
|
2118
2118
|
"QFAI-TRACE-021",
|
|
2119
2119
|
`Spec \u306E\u5951\u7D04 ID \u304C\u4E0D\u6B63\u3067\u3059: ${contractRefs.invalidTokens.join(
|
|
2120
2120
|
", "
|
|
2121
|
-
)}`,
|
|
2121
|
+
)} (\u4F8B: UI-0001 / API-0001 / DB-0001)`,
|
|
2122
2122
|
"error",
|
|
2123
2123
|
file,
|
|
2124
2124
|
"traceability.specContractRefFormat",
|
|
@@ -2158,7 +2158,7 @@ async function validateTraceability(root, config) {
|
|
|
2158
2158
|
issues.push(
|
|
2159
2159
|
issue6(
|
|
2160
2160
|
"QFAI-TRACE-031",
|
|
2161
|
-
"Scenario \u306B QFAI-CONTRACT-REF \u304C\u3042\u308A\u307E\u305B\u3093\u3002",
|
|
2161
|
+
"Scenario \u306B QFAI-CONTRACT-REF \u304C\u3042\u308A\u307E\u305B\u3093\u3002\u4F8B: `# QFAI-CONTRACT-REF: none` \u307E\u305F\u306F `# QFAI-CONTRACT-REF: UI-0001`",
|
|
2162
2162
|
"error",
|
|
2163
2163
|
file,
|
|
2164
2164
|
"traceability.scenarioContractRefRequired"
|
|
@@ -2169,7 +2169,7 @@ async function validateTraceability(root, config) {
|
|
|
2169
2169
|
issues.push(
|
|
2170
2170
|
issue6(
|
|
2171
2171
|
"QFAI-TRACE-033",
|
|
2172
|
-
"Scenario \u306E QFAI-CONTRACT-REF \u306B none \u3068\u5951\u7D04 ID \u304C\u6DF7\u5728\u3057\u3066\u3044\u307E\u3059\u3002",
|
|
2172
|
+
"Scenario \u306E QFAI-CONTRACT-REF \u306B none \u3068\u5951\u7D04 ID \u304C\u6DF7\u5728\u3057\u3066\u3044\u307E\u3059\u3002none \u304B \u5951\u7D04ID \u306E\u3069\u3061\u3089\u304B\u4E00\u65B9\u3060\u3051\u306B\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
2173
2173
|
"error",
|
|
2174
2174
|
file,
|
|
2175
2175
|
"traceability.scenarioContractRefFormat"
|
|
@@ -2182,7 +2182,7 @@ async function validateTraceability(root, config) {
|
|
|
2182
2182
|
"QFAI-TRACE-032",
|
|
2183
2183
|
`Scenario \u306E\u5951\u7D04 ID \u304C\u4E0D\u6B63\u3067\u3059: ${scenarioContractRefs.invalidTokens.join(
|
|
2184
2184
|
", "
|
|
2185
|
-
)}`,
|
|
2185
|
+
)} (\u4F8B: UI-0001 / API-0001 / DB-0001)`,
|
|
2186
2186
|
"error",
|
|
2187
2187
|
file,
|
|
2188
2188
|
"traceability.scenarioContractRefFormat",
|
|
@@ -2680,7 +2680,7 @@ function formatReportMarkdown(data) {
|
|
|
2680
2680
|
lines.push(`- \u8A2D\u5B9A: ${data.configPath}`);
|
|
2681
2681
|
lines.push(`- \u7248: ${data.version}`);
|
|
2682
2682
|
lines.push("");
|
|
2683
|
-
lines.push("##
|
|
2683
|
+
lines.push("## Summary");
|
|
2684
2684
|
lines.push("");
|
|
2685
2685
|
lines.push(`- specs: ${data.summary.specs}`);
|
|
2686
2686
|
lines.push(`- scenarios: ${data.summary.scenarios}`);
|
|
@@ -2690,8 +2690,79 @@ function formatReportMarkdown(data) {
|
|
|
2690
2690
|
lines.push(
|
|
2691
2691
|
`- issues: info ${data.summary.counts.info} / warning ${data.summary.counts.warning} / error ${data.summary.counts.error}`
|
|
2692
2692
|
);
|
|
2693
|
+
lines.push(
|
|
2694
|
+
`- fail-on=error: ${data.summary.counts.error > 0 ? "FAIL" : "PASS"}`
|
|
2695
|
+
);
|
|
2696
|
+
lines.push(
|
|
2697
|
+
`- fail-on=warning: ${data.summary.counts.error + data.summary.counts.warning > 0 ? "FAIL" : "PASS"}`
|
|
2698
|
+
);
|
|
2693
2699
|
lines.push("");
|
|
2694
|
-
lines.push("##
|
|
2700
|
+
lines.push("## Findings");
|
|
2701
|
+
lines.push("");
|
|
2702
|
+
lines.push("### Issues (by code)");
|
|
2703
|
+
lines.push("");
|
|
2704
|
+
const severityOrder = {
|
|
2705
|
+
error: 0,
|
|
2706
|
+
warning: 1,
|
|
2707
|
+
info: 2
|
|
2708
|
+
};
|
|
2709
|
+
const issueKeyToCount = /* @__PURE__ */ new Map();
|
|
2710
|
+
for (const issue7 of data.issues) {
|
|
2711
|
+
const key = `${issue7.severity}|${issue7.code}`;
|
|
2712
|
+
const current = issueKeyToCount.get(key);
|
|
2713
|
+
if (current) {
|
|
2714
|
+
current.count += 1;
|
|
2715
|
+
continue;
|
|
2716
|
+
}
|
|
2717
|
+
issueKeyToCount.set(key, {
|
|
2718
|
+
severity: issue7.severity,
|
|
2719
|
+
code: issue7.code,
|
|
2720
|
+
count: 1
|
|
2721
|
+
});
|
|
2722
|
+
}
|
|
2723
|
+
const issueSummaryRows = Array.from(issueKeyToCount.values()).sort((a, b) => {
|
|
2724
|
+
const sa = severityOrder[a.severity] ?? 999;
|
|
2725
|
+
const sb = severityOrder[b.severity] ?? 999;
|
|
2726
|
+
if (sa !== sb) return sa - sb;
|
|
2727
|
+
return a.code.localeCompare(b.code);
|
|
2728
|
+
}).map((x) => [x.severity, x.code, String(x.count)]);
|
|
2729
|
+
if (issueSummaryRows.length === 0) {
|
|
2730
|
+
lines.push("- (none)");
|
|
2731
|
+
} else {
|
|
2732
|
+
lines.push(
|
|
2733
|
+
...formatMarkdownTable(["Severity", "Code", "Count"], issueSummaryRows)
|
|
2734
|
+
);
|
|
2735
|
+
}
|
|
2736
|
+
lines.push("");
|
|
2737
|
+
lines.push("### Issues (list)");
|
|
2738
|
+
lines.push("");
|
|
2739
|
+
if (data.issues.length === 0) {
|
|
2740
|
+
lines.push("- (none)");
|
|
2741
|
+
} else {
|
|
2742
|
+
const sortedIssues = [...data.issues].sort((a, b) => {
|
|
2743
|
+
const sa = severityOrder[a.severity] ?? 999;
|
|
2744
|
+
const sb = severityOrder[b.severity] ?? 999;
|
|
2745
|
+
if (sa !== sb) return sa - sb;
|
|
2746
|
+
const code = a.code.localeCompare(b.code);
|
|
2747
|
+
if (code !== 0) return code;
|
|
2748
|
+
const fileA = a.file ?? "";
|
|
2749
|
+
const fileB = b.file ?? "";
|
|
2750
|
+
const file = fileA.localeCompare(fileB);
|
|
2751
|
+
if (file !== 0) return file;
|
|
2752
|
+
const lineA = a.loc?.line ?? 0;
|
|
2753
|
+
const lineB = b.loc?.line ?? 0;
|
|
2754
|
+
return lineA - lineB;
|
|
2755
|
+
});
|
|
2756
|
+
for (const item of sortedIssues) {
|
|
2757
|
+
const location = item.file ? ` (${item.file})` : "";
|
|
2758
|
+
const refs = item.refs && item.refs.length > 0 ? ` refs=${item.refs.join(",")}` : "";
|
|
2759
|
+
lines.push(
|
|
2760
|
+
`- ${item.severity.toUpperCase()} [${item.code}] ${item.message}${location}${refs}`
|
|
2761
|
+
);
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2764
|
+
lines.push("");
|
|
2765
|
+
lines.push("### IDs");
|
|
2695
2766
|
lines.push("");
|
|
2696
2767
|
lines.push(formatIdLine("SPEC", data.ids.spec));
|
|
2697
2768
|
lines.push(formatIdLine("BR", data.ids.br));
|
|
@@ -2700,14 +2771,14 @@ function formatReportMarkdown(data) {
|
|
|
2700
2771
|
lines.push(formatIdLine("API", data.ids.api));
|
|
2701
2772
|
lines.push(formatIdLine("DB", data.ids.db));
|
|
2702
2773
|
lines.push("");
|
|
2703
|
-
lines.push("
|
|
2774
|
+
lines.push("### Traceability");
|
|
2704
2775
|
lines.push("");
|
|
2705
2776
|
lines.push(`- \u4E0A\u6D41ID\u691C\u51FA\u6570: ${data.traceability.upstreamIdsFound}`);
|
|
2706
2777
|
lines.push(
|
|
2707
2778
|
`- \u30B3\u30FC\u30C9/\u30C6\u30B9\u30C8\u53C2\u7167: ${data.traceability.referencedInCodeOrTests ? "\u3042\u308A" : "\u306A\u3057"}`
|
|
2708
2779
|
);
|
|
2709
2780
|
lines.push("");
|
|
2710
|
-
lines.push("
|
|
2781
|
+
lines.push("### Contract Coverage");
|
|
2711
2782
|
lines.push("");
|
|
2712
2783
|
lines.push(`- total: ${data.traceability.contracts.total}`);
|
|
2713
2784
|
lines.push(`- referenced: ${data.traceability.contracts.referenced}`);
|
|
@@ -2716,7 +2787,7 @@ function formatReportMarkdown(data) {
|
|
|
2716
2787
|
`- specContractRefMissing: ${data.traceability.specs.contractRefMissing}`
|
|
2717
2788
|
);
|
|
2718
2789
|
lines.push("");
|
|
2719
|
-
lines.push("
|
|
2790
|
+
lines.push("### Contract \u2192 Spec");
|
|
2720
2791
|
lines.push("");
|
|
2721
2792
|
const contractToSpecs = data.traceability.contracts.idToSpecs;
|
|
2722
2793
|
const contractIds = Object.keys(contractToSpecs).sort(
|
|
@@ -2735,7 +2806,7 @@ function formatReportMarkdown(data) {
|
|
|
2735
2806
|
}
|
|
2736
2807
|
}
|
|
2737
2808
|
lines.push("");
|
|
2738
|
-
lines.push("
|
|
2809
|
+
lines.push("### Spec \u2192 Contracts");
|
|
2739
2810
|
lines.push("");
|
|
2740
2811
|
const specToContracts = data.traceability.specs.specToContracts;
|
|
2741
2812
|
const specIds = Object.keys(specToContracts).sort(
|
|
@@ -2753,7 +2824,7 @@ function formatReportMarkdown(data) {
|
|
|
2753
2824
|
lines.push(...formatMarkdownTable(["Spec", "Status", "Contracts"], rows));
|
|
2754
2825
|
}
|
|
2755
2826
|
lines.push("");
|
|
2756
|
-
lines.push("
|
|
2827
|
+
lines.push("### Specs missing contract-ref");
|
|
2757
2828
|
lines.push("");
|
|
2758
2829
|
const missingRefSpecs = data.traceability.specs.missingRefSpecs;
|
|
2759
2830
|
if (missingRefSpecs.length === 0) {
|
|
@@ -2764,7 +2835,7 @@ function formatReportMarkdown(data) {
|
|
|
2764
2835
|
}
|
|
2765
2836
|
}
|
|
2766
2837
|
lines.push("");
|
|
2767
|
-
lines.push("
|
|
2838
|
+
lines.push("### SC coverage");
|
|
2768
2839
|
lines.push("");
|
|
2769
2840
|
lines.push(`- total: ${data.traceability.sc.total}`);
|
|
2770
2841
|
lines.push(`- covered: ${data.traceability.sc.covered}`);
|
|
@@ -2794,7 +2865,7 @@ function formatReportMarkdown(data) {
|
|
|
2794
2865
|
lines.push(`- missingIds: ${missingWithSources.join(", ")}`);
|
|
2795
2866
|
}
|
|
2796
2867
|
lines.push("");
|
|
2797
|
-
lines.push("
|
|
2868
|
+
lines.push("### SC \u2192 referenced tests");
|
|
2798
2869
|
lines.push("");
|
|
2799
2870
|
const scRefs = data.traceability.sc.refs;
|
|
2800
2871
|
const scIds = Object.keys(scRefs).sort((a, b) => a.localeCompare(b));
|
|
@@ -2811,7 +2882,7 @@ function formatReportMarkdown(data) {
|
|
|
2811
2882
|
}
|
|
2812
2883
|
}
|
|
2813
2884
|
lines.push("");
|
|
2814
|
-
lines.push("
|
|
2885
|
+
lines.push("### Spec:SC=1:1 violations");
|
|
2815
2886
|
lines.push("");
|
|
2816
2887
|
const specScIssues = data.issues.filter(
|
|
2817
2888
|
(item) => item.code === "QFAI-TRACE-012"
|
|
@@ -2826,7 +2897,7 @@ function formatReportMarkdown(data) {
|
|
|
2826
2897
|
}
|
|
2827
2898
|
}
|
|
2828
2899
|
lines.push("");
|
|
2829
|
-
lines.push("
|
|
2900
|
+
lines.push("### Hotspots");
|
|
2830
2901
|
lines.push("");
|
|
2831
2902
|
const hotspots = buildHotspots(data.issues);
|
|
2832
2903
|
if (hotspots.length === 0) {
|
|
@@ -2839,35 +2910,28 @@ function formatReportMarkdown(data) {
|
|
|
2839
2910
|
}
|
|
2840
2911
|
}
|
|
2841
2912
|
lines.push("");
|
|
2842
|
-
lines.push("##
|
|
2913
|
+
lines.push("## Guidance");
|
|
2843
2914
|
lines.push("");
|
|
2844
|
-
|
|
2845
|
-
|
|
2915
|
+
lines.push(
|
|
2916
|
+
"- \u6B21\u306E\u624B\u9806: `qfai doctor --fail-on error` \u2192 `qfai validate --fail-on error` \u2192 `qfai report`"
|
|
2846
2917
|
);
|
|
2847
|
-
if (
|
|
2848
|
-
lines.push("-
|
|
2849
|
-
} else {
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
`- ${item.severity.toUpperCase()} [${item.code}] ${item.message}${location}`
|
|
2854
|
-
);
|
|
2855
|
-
}
|
|
2856
|
-
}
|
|
2857
|
-
lines.push("");
|
|
2858
|
-
lines.push("## \u691C\u8A3C\u7D50\u679C");
|
|
2859
|
-
lines.push("");
|
|
2860
|
-
if (data.issues.length === 0) {
|
|
2861
|
-
lines.push("- (none)");
|
|
2918
|
+
if (data.summary.counts.error > 0) {
|
|
2919
|
+
lines.push("- error \u304C\u3042\u308B\u305F\u3081\u3001\u307E\u305A error \u304B\u3089\u4FEE\u6B63\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
|
|
2920
|
+
} else if (data.summary.counts.warning > 0) {
|
|
2921
|
+
lines.push(
|
|
2922
|
+
"- warning \u306E\u6271\u3044\uFF08Hard Gate \u306B\u3059\u308B\u304B\uFF09\u306F\u904B\u7528\u3067\u6C7A\u3081\u3066\u304F\u3060\u3055\u3044\u3002"
|
|
2923
|
+
);
|
|
2862
2924
|
} else {
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
lines.push(
|
|
2867
|
-
`- ${item.severity.toUpperCase()} [${item.code}] ${item.message}${location}${refs}`
|
|
2868
|
-
);
|
|
2869
|
-
}
|
|
2925
|
+
lines.push(
|
|
2926
|
+
"- issue \u306F\u691C\u51FA\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u904B\u7528\u30C6\u30F3\u30D7\u30EC\u306B\u6CBF\u3063\u3066\u7D99\u7D9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
|
|
2927
|
+
);
|
|
2870
2928
|
}
|
|
2929
|
+
lines.push(
|
|
2930
|
+
"- \u5909\u66F4\u533A\u5206\uFF08Compatibility / Change/Improvement\uFF09\u306F `.qfai/specs/*/delta.md` \u306B\u8A18\u9332\u3057\u307E\u3059\u3002"
|
|
2931
|
+
);
|
|
2932
|
+
lines.push(
|
|
2933
|
+
"- \u53C2\u7167\u30EB\u30FC\u30EB\u306E\u6B63\u672C: `.qfai/promptpack/steering/traceability.md` / `.qfai/promptpack/steering/compatibility-vs-change.md`"
|
|
2934
|
+
);
|
|
2871
2935
|
return lines.join("\n");
|
|
2872
2936
|
}
|
|
2873
2937
|
function formatReportJson(data) {
|