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.cjs
CHANGED
|
@@ -1232,8 +1232,8 @@ var import_promises7 = require("fs/promises");
|
|
|
1232
1232
|
var import_node_path7 = __toESM(require("path"), 1);
|
|
1233
1233
|
var import_node_url = require("url");
|
|
1234
1234
|
async function resolveToolVersion() {
|
|
1235
|
-
if ("0.
|
|
1236
|
-
return "0.
|
|
1235
|
+
if ("0.8.0".length > 0) {
|
|
1236
|
+
return "0.8.0";
|
|
1237
1237
|
}
|
|
1238
1238
|
try {
|
|
1239
1239
|
const packagePath = resolvePackageJsonPath();
|
|
@@ -1604,7 +1604,7 @@ async function validateDeltas(root, config) {
|
|
|
1604
1604
|
issues.push(
|
|
1605
1605
|
issue2(
|
|
1606
1606
|
"QFAI-DELTA-002",
|
|
1607
|
-
"delta.md \u306E\u5909\u66F4\u533A\u5206\u304C\u4E0D\u8DB3\u3057\u3066\u3044\u307E\u3059\u3002",
|
|
1607
|
+
"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",
|
|
1608
1608
|
"error",
|
|
1609
1609
|
deltaPath,
|
|
1610
1610
|
"delta.section"
|
|
@@ -2147,7 +2147,7 @@ async function validateTraceability(root, config) {
|
|
|
2147
2147
|
issues.push(
|
|
2148
2148
|
issue6(
|
|
2149
2149
|
"QFAI-TRACE-020",
|
|
2150
|
-
"Spec \u306B QFAI-CONTRACT-REF \u304C\u3042\u308A\u307E\u305B\u3093\u3002",
|
|
2150
|
+
"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`",
|
|
2151
2151
|
"error",
|
|
2152
2152
|
file,
|
|
2153
2153
|
"traceability.specContractRefRequired"
|
|
@@ -2158,7 +2158,7 @@ async function validateTraceability(root, config) {
|
|
|
2158
2158
|
issues.push(
|
|
2159
2159
|
issue6(
|
|
2160
2160
|
"QFAI-TRACE-023",
|
|
2161
|
-
"Spec \u306E QFAI-CONTRACT-REF \u306B none \u3068\u5951\u7D04 ID \u304C\u6DF7\u5728\u3057\u3066\u3044\u307E\u3059\u3002",
|
|
2161
|
+
"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",
|
|
2162
2162
|
"error",
|
|
2163
2163
|
file,
|
|
2164
2164
|
"traceability.specContractRefFormat"
|
|
@@ -2171,7 +2171,7 @@ async function validateTraceability(root, config) {
|
|
|
2171
2171
|
"QFAI-TRACE-021",
|
|
2172
2172
|
`Spec \u306E\u5951\u7D04 ID \u304C\u4E0D\u6B63\u3067\u3059: ${contractRefs.invalidTokens.join(
|
|
2173
2173
|
", "
|
|
2174
|
-
)}`,
|
|
2174
|
+
)} (\u4F8B: UI-0001 / API-0001 / DB-0001)`,
|
|
2175
2175
|
"error",
|
|
2176
2176
|
file,
|
|
2177
2177
|
"traceability.specContractRefFormat",
|
|
@@ -2211,7 +2211,7 @@ async function validateTraceability(root, config) {
|
|
|
2211
2211
|
issues.push(
|
|
2212
2212
|
issue6(
|
|
2213
2213
|
"QFAI-TRACE-031",
|
|
2214
|
-
"Scenario \u306B QFAI-CONTRACT-REF \u304C\u3042\u308A\u307E\u305B\u3093\u3002",
|
|
2214
|
+
"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`",
|
|
2215
2215
|
"error",
|
|
2216
2216
|
file,
|
|
2217
2217
|
"traceability.scenarioContractRefRequired"
|
|
@@ -2222,7 +2222,7 @@ async function validateTraceability(root, config) {
|
|
|
2222
2222
|
issues.push(
|
|
2223
2223
|
issue6(
|
|
2224
2224
|
"QFAI-TRACE-033",
|
|
2225
|
-
"Scenario \u306E QFAI-CONTRACT-REF \u306B none \u3068\u5951\u7D04 ID \u304C\u6DF7\u5728\u3057\u3066\u3044\u307E\u3059\u3002",
|
|
2225
|
+
"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",
|
|
2226
2226
|
"error",
|
|
2227
2227
|
file,
|
|
2228
2228
|
"traceability.scenarioContractRefFormat"
|
|
@@ -2235,7 +2235,7 @@ async function validateTraceability(root, config) {
|
|
|
2235
2235
|
"QFAI-TRACE-032",
|
|
2236
2236
|
`Scenario \u306E\u5951\u7D04 ID \u304C\u4E0D\u6B63\u3067\u3059: ${scenarioContractRefs.invalidTokens.join(
|
|
2237
2237
|
", "
|
|
2238
|
-
)}`,
|
|
2238
|
+
)} (\u4F8B: UI-0001 / API-0001 / DB-0001)`,
|
|
2239
2239
|
"error",
|
|
2240
2240
|
file,
|
|
2241
2241
|
"traceability.scenarioContractRefFormat",
|
|
@@ -2733,7 +2733,7 @@ function formatReportMarkdown(data) {
|
|
|
2733
2733
|
lines.push(`- \u8A2D\u5B9A: ${data.configPath}`);
|
|
2734
2734
|
lines.push(`- \u7248: ${data.version}`);
|
|
2735
2735
|
lines.push("");
|
|
2736
|
-
lines.push("##
|
|
2736
|
+
lines.push("## Summary");
|
|
2737
2737
|
lines.push("");
|
|
2738
2738
|
lines.push(`- specs: ${data.summary.specs}`);
|
|
2739
2739
|
lines.push(`- scenarios: ${data.summary.scenarios}`);
|
|
@@ -2743,8 +2743,79 @@ function formatReportMarkdown(data) {
|
|
|
2743
2743
|
lines.push(
|
|
2744
2744
|
`- issues: info ${data.summary.counts.info} / warning ${data.summary.counts.warning} / error ${data.summary.counts.error}`
|
|
2745
2745
|
);
|
|
2746
|
+
lines.push(
|
|
2747
|
+
`- fail-on=error: ${data.summary.counts.error > 0 ? "FAIL" : "PASS"}`
|
|
2748
|
+
);
|
|
2749
|
+
lines.push(
|
|
2750
|
+
`- fail-on=warning: ${data.summary.counts.error + data.summary.counts.warning > 0 ? "FAIL" : "PASS"}`
|
|
2751
|
+
);
|
|
2746
2752
|
lines.push("");
|
|
2747
|
-
lines.push("##
|
|
2753
|
+
lines.push("## Findings");
|
|
2754
|
+
lines.push("");
|
|
2755
|
+
lines.push("### Issues (by code)");
|
|
2756
|
+
lines.push("");
|
|
2757
|
+
const severityOrder = {
|
|
2758
|
+
error: 0,
|
|
2759
|
+
warning: 1,
|
|
2760
|
+
info: 2
|
|
2761
|
+
};
|
|
2762
|
+
const issueKeyToCount = /* @__PURE__ */ new Map();
|
|
2763
|
+
for (const issue7 of data.issues) {
|
|
2764
|
+
const key = `${issue7.severity}|${issue7.code}`;
|
|
2765
|
+
const current = issueKeyToCount.get(key);
|
|
2766
|
+
if (current) {
|
|
2767
|
+
current.count += 1;
|
|
2768
|
+
continue;
|
|
2769
|
+
}
|
|
2770
|
+
issueKeyToCount.set(key, {
|
|
2771
|
+
severity: issue7.severity,
|
|
2772
|
+
code: issue7.code,
|
|
2773
|
+
count: 1
|
|
2774
|
+
});
|
|
2775
|
+
}
|
|
2776
|
+
const issueSummaryRows = Array.from(issueKeyToCount.values()).sort((a, b) => {
|
|
2777
|
+
const sa = severityOrder[a.severity] ?? 999;
|
|
2778
|
+
const sb = severityOrder[b.severity] ?? 999;
|
|
2779
|
+
if (sa !== sb) return sa - sb;
|
|
2780
|
+
return a.code.localeCompare(b.code);
|
|
2781
|
+
}).map((x) => [x.severity, x.code, String(x.count)]);
|
|
2782
|
+
if (issueSummaryRows.length === 0) {
|
|
2783
|
+
lines.push("- (none)");
|
|
2784
|
+
} else {
|
|
2785
|
+
lines.push(
|
|
2786
|
+
...formatMarkdownTable(["Severity", "Code", "Count"], issueSummaryRows)
|
|
2787
|
+
);
|
|
2788
|
+
}
|
|
2789
|
+
lines.push("");
|
|
2790
|
+
lines.push("### Issues (list)");
|
|
2791
|
+
lines.push("");
|
|
2792
|
+
if (data.issues.length === 0) {
|
|
2793
|
+
lines.push("- (none)");
|
|
2794
|
+
} else {
|
|
2795
|
+
const sortedIssues = [...data.issues].sort((a, b) => {
|
|
2796
|
+
const sa = severityOrder[a.severity] ?? 999;
|
|
2797
|
+
const sb = severityOrder[b.severity] ?? 999;
|
|
2798
|
+
if (sa !== sb) return sa - sb;
|
|
2799
|
+
const code = a.code.localeCompare(b.code);
|
|
2800
|
+
if (code !== 0) return code;
|
|
2801
|
+
const fileA = a.file ?? "";
|
|
2802
|
+
const fileB = b.file ?? "";
|
|
2803
|
+
const file = fileA.localeCompare(fileB);
|
|
2804
|
+
if (file !== 0) return file;
|
|
2805
|
+
const lineA = a.loc?.line ?? 0;
|
|
2806
|
+
const lineB = b.loc?.line ?? 0;
|
|
2807
|
+
return lineA - lineB;
|
|
2808
|
+
});
|
|
2809
|
+
for (const item of sortedIssues) {
|
|
2810
|
+
const location = item.file ? ` (${item.file})` : "";
|
|
2811
|
+
const refs = item.refs && item.refs.length > 0 ? ` refs=${item.refs.join(",")}` : "";
|
|
2812
|
+
lines.push(
|
|
2813
|
+
`- ${item.severity.toUpperCase()} [${item.code}] ${item.message}${location}${refs}`
|
|
2814
|
+
);
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
lines.push("");
|
|
2818
|
+
lines.push("### IDs");
|
|
2748
2819
|
lines.push("");
|
|
2749
2820
|
lines.push(formatIdLine("SPEC", data.ids.spec));
|
|
2750
2821
|
lines.push(formatIdLine("BR", data.ids.br));
|
|
@@ -2753,14 +2824,14 @@ function formatReportMarkdown(data) {
|
|
|
2753
2824
|
lines.push(formatIdLine("API", data.ids.api));
|
|
2754
2825
|
lines.push(formatIdLine("DB", data.ids.db));
|
|
2755
2826
|
lines.push("");
|
|
2756
|
-
lines.push("
|
|
2827
|
+
lines.push("### Traceability");
|
|
2757
2828
|
lines.push("");
|
|
2758
2829
|
lines.push(`- \u4E0A\u6D41ID\u691C\u51FA\u6570: ${data.traceability.upstreamIdsFound}`);
|
|
2759
2830
|
lines.push(
|
|
2760
2831
|
`- \u30B3\u30FC\u30C9/\u30C6\u30B9\u30C8\u53C2\u7167: ${data.traceability.referencedInCodeOrTests ? "\u3042\u308A" : "\u306A\u3057"}`
|
|
2761
2832
|
);
|
|
2762
2833
|
lines.push("");
|
|
2763
|
-
lines.push("
|
|
2834
|
+
lines.push("### Contract Coverage");
|
|
2764
2835
|
lines.push("");
|
|
2765
2836
|
lines.push(`- total: ${data.traceability.contracts.total}`);
|
|
2766
2837
|
lines.push(`- referenced: ${data.traceability.contracts.referenced}`);
|
|
@@ -2769,7 +2840,7 @@ function formatReportMarkdown(data) {
|
|
|
2769
2840
|
`- specContractRefMissing: ${data.traceability.specs.contractRefMissing}`
|
|
2770
2841
|
);
|
|
2771
2842
|
lines.push("");
|
|
2772
|
-
lines.push("
|
|
2843
|
+
lines.push("### Contract \u2192 Spec");
|
|
2773
2844
|
lines.push("");
|
|
2774
2845
|
const contractToSpecs = data.traceability.contracts.idToSpecs;
|
|
2775
2846
|
const contractIds = Object.keys(contractToSpecs).sort(
|
|
@@ -2788,7 +2859,7 @@ function formatReportMarkdown(data) {
|
|
|
2788
2859
|
}
|
|
2789
2860
|
}
|
|
2790
2861
|
lines.push("");
|
|
2791
|
-
lines.push("
|
|
2862
|
+
lines.push("### Spec \u2192 Contracts");
|
|
2792
2863
|
lines.push("");
|
|
2793
2864
|
const specToContracts = data.traceability.specs.specToContracts;
|
|
2794
2865
|
const specIds = Object.keys(specToContracts).sort(
|
|
@@ -2806,7 +2877,7 @@ function formatReportMarkdown(data) {
|
|
|
2806
2877
|
lines.push(...formatMarkdownTable(["Spec", "Status", "Contracts"], rows));
|
|
2807
2878
|
}
|
|
2808
2879
|
lines.push("");
|
|
2809
|
-
lines.push("
|
|
2880
|
+
lines.push("### Specs missing contract-ref");
|
|
2810
2881
|
lines.push("");
|
|
2811
2882
|
const missingRefSpecs = data.traceability.specs.missingRefSpecs;
|
|
2812
2883
|
if (missingRefSpecs.length === 0) {
|
|
@@ -2817,7 +2888,7 @@ function formatReportMarkdown(data) {
|
|
|
2817
2888
|
}
|
|
2818
2889
|
}
|
|
2819
2890
|
lines.push("");
|
|
2820
|
-
lines.push("
|
|
2891
|
+
lines.push("### SC coverage");
|
|
2821
2892
|
lines.push("");
|
|
2822
2893
|
lines.push(`- total: ${data.traceability.sc.total}`);
|
|
2823
2894
|
lines.push(`- covered: ${data.traceability.sc.covered}`);
|
|
@@ -2847,7 +2918,7 @@ function formatReportMarkdown(data) {
|
|
|
2847
2918
|
lines.push(`- missingIds: ${missingWithSources.join(", ")}`);
|
|
2848
2919
|
}
|
|
2849
2920
|
lines.push("");
|
|
2850
|
-
lines.push("
|
|
2921
|
+
lines.push("### SC \u2192 referenced tests");
|
|
2851
2922
|
lines.push("");
|
|
2852
2923
|
const scRefs = data.traceability.sc.refs;
|
|
2853
2924
|
const scIds = Object.keys(scRefs).sort((a, b) => a.localeCompare(b));
|
|
@@ -2864,7 +2935,7 @@ function formatReportMarkdown(data) {
|
|
|
2864
2935
|
}
|
|
2865
2936
|
}
|
|
2866
2937
|
lines.push("");
|
|
2867
|
-
lines.push("
|
|
2938
|
+
lines.push("### Spec:SC=1:1 violations");
|
|
2868
2939
|
lines.push("");
|
|
2869
2940
|
const specScIssues = data.issues.filter(
|
|
2870
2941
|
(item) => item.code === "QFAI-TRACE-012"
|
|
@@ -2879,7 +2950,7 @@ function formatReportMarkdown(data) {
|
|
|
2879
2950
|
}
|
|
2880
2951
|
}
|
|
2881
2952
|
lines.push("");
|
|
2882
|
-
lines.push("
|
|
2953
|
+
lines.push("### Hotspots");
|
|
2883
2954
|
lines.push("");
|
|
2884
2955
|
const hotspots = buildHotspots(data.issues);
|
|
2885
2956
|
if (hotspots.length === 0) {
|
|
@@ -2892,35 +2963,28 @@ function formatReportMarkdown(data) {
|
|
|
2892
2963
|
}
|
|
2893
2964
|
}
|
|
2894
2965
|
lines.push("");
|
|
2895
|
-
lines.push("##
|
|
2966
|
+
lines.push("## Guidance");
|
|
2896
2967
|
lines.push("");
|
|
2897
|
-
|
|
2898
|
-
|
|
2968
|
+
lines.push(
|
|
2969
|
+
"- \u6B21\u306E\u624B\u9806: `qfai doctor --fail-on error` \u2192 `qfai validate --fail-on error` \u2192 `qfai report`"
|
|
2899
2970
|
);
|
|
2900
|
-
if (
|
|
2901
|
-
lines.push("-
|
|
2902
|
-
} else {
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
`- ${item.severity.toUpperCase()} [${item.code}] ${item.message}${location}`
|
|
2907
|
-
);
|
|
2908
|
-
}
|
|
2909
|
-
}
|
|
2910
|
-
lines.push("");
|
|
2911
|
-
lines.push("## \u691C\u8A3C\u7D50\u679C");
|
|
2912
|
-
lines.push("");
|
|
2913
|
-
if (data.issues.length === 0) {
|
|
2914
|
-
lines.push("- (none)");
|
|
2971
|
+
if (data.summary.counts.error > 0) {
|
|
2972
|
+
lines.push("- error \u304C\u3042\u308B\u305F\u3081\u3001\u307E\u305A error \u304B\u3089\u4FEE\u6B63\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
|
|
2973
|
+
} else if (data.summary.counts.warning > 0) {
|
|
2974
|
+
lines.push(
|
|
2975
|
+
"- warning \u306E\u6271\u3044\uFF08Hard Gate \u306B\u3059\u308B\u304B\uFF09\u306F\u904B\u7528\u3067\u6C7A\u3081\u3066\u304F\u3060\u3055\u3044\u3002"
|
|
2976
|
+
);
|
|
2915
2977
|
} else {
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
lines.push(
|
|
2920
|
-
`- ${item.severity.toUpperCase()} [${item.code}] ${item.message}${location}${refs}`
|
|
2921
|
-
);
|
|
2922
|
-
}
|
|
2978
|
+
lines.push(
|
|
2979
|
+
"- 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"
|
|
2980
|
+
);
|
|
2923
2981
|
}
|
|
2982
|
+
lines.push(
|
|
2983
|
+
"- \u5909\u66F4\u533A\u5206\uFF08Compatibility / Change/Improvement\uFF09\u306F `.qfai/specs/*/delta.md` \u306B\u8A18\u9332\u3057\u307E\u3059\u3002"
|
|
2984
|
+
);
|
|
2985
|
+
lines.push(
|
|
2986
|
+
"- \u53C2\u7167\u30EB\u30FC\u30EB\u306E\u6B63\u672C: `.qfai/promptpack/steering/traceability.md` / `.qfai/promptpack/steering/compatibility-vs-change.md`"
|
|
2987
|
+
);
|
|
2924
2988
|
return lines.join("\n");
|
|
2925
2989
|
}
|
|
2926
2990
|
function formatReportJson(data) {
|