qfai 2.1.0 → 2.1.1
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 +272 -20
- package/assets/defaults/workflows/change-tooling.yml +0 -4
- package/assets/init/.claude/settings.json +8 -8
- package/assets/init/.codex/hooks.json +6 -6
- package/assets/init/.qfai/assistant/agent/implementation-reviewer.md +14 -1
- package/assets/init/.qfai/assistant/agent/qa-gatekeeper.md +29 -0
- package/assets/init/.qfai/assistant/rule/constitution.md +5 -4
- package/assets/init/.qfai/assistant/rule/drift-protocol.md +14 -3
- package/assets/init/.qfai/assistant/rule/references/asset-authoring.md +70 -0
- package/assets/init/.qfai/assistant/rule/references/context-summary.md +30 -0
- package/assets/init/.qfai/assistant/rule/references/gate-failure-autorepair.md +42 -0
- package/assets/init/.qfai/assistant/rule/references/griller-recommendations.md +23 -0
- package/assets/init/.qfai/assistant/rule/references/host-backstops.md +60 -0
- package/assets/init/.qfai/assistant/rule/references/placeholder-scan.md +25 -0
- package/assets/init/.qfai/assistant/rule/references/reviewer-findings.md +41 -0
- package/assets/init/.qfai/assistant/rule/references/reviewer-remit.md +35 -0
- package/assets/init/.qfai/assistant/rule/shared-skill-delegation-baseline.md +26 -139
- package/assets/init/.qfai/assistant/rule/shared-skill-operating-baseline.md +6 -137
- package/assets/init/.qfai/assistant/rule/stage-cost.md +1 -1
- package/assets/init/.qfai/assistant/rule/test-layers.md +42 -0
- package/assets/init/.qfai/assistant/skill/qfai-configure/SKILL.md +8 -42
- package/assets/init/.qfai/assistant/skill/qfai-configure/references/principles.md +25 -0
- package/assets/init/.qfai/assistant/skill/qfai-discussion/SKILL.md +1 -1
- package/assets/init/.qfai/assistant/skill/qfai-discussion/references/discussion-completion-matrix.md +9 -2
- package/assets/init/.qfai/assistant/skill/qfai-discussion/references/ui_ux/platform_baselines.md +11 -0
- package/assets/init/.qfai/assistant/skill/qfai-discussion/references/ui_ux/review_audit_playbook.md +11 -0
- package/assets/init/.qfai/assistant/skill/qfai-discussion/references/ui_ux_best_practices.md +12 -36
- package/assets/init/.qfai/assistant/skill/qfai-grill/SKILL.md +1 -1
- package/assets/init/.qfai/assistant/skill/qfai-grilling/SKILL.md +16 -88
- package/assets/init/.qfai/assistant/skill/qfai-grilling/references/delegated-session.md +58 -0
- package/assets/init/.qfai/assistant/skill/qfai-grilling/references/session-record.md +42 -0
- package/assets/init/.qfai/assistant/skill/qfai-implement/SKILL.md +8 -1
- package/assets/init/.qfai/assistant/skill/qfai-implement/references/credential-reuse.md +9 -0
- package/assets/init/.qfai/assistant/skill/qfai-implement/references/cross-spec-ownership.md +41 -0
- package/assets/init/.qfai/assistant/skill/qfai-implement/references/oracle-strength.md +6 -1
- package/assets/init/.qfai/assistant/skill/qfai-implement/references/red-admissibility.md +1 -1
- package/assets/init/.qfai/assistant/skill/qfai-implement/references/ui-affecting.md +1 -3
- package/assets/init/.qfai/assistant/skill/qfai-maintain/SKILL.md +1 -1
- package/assets/init/.qfai/assistant/skill/qfai-migration-v1-to-v2/SKILL.md +4 -4
- package/assets/init/.qfai/assistant/skill/qfai-migration-v1-to-v2/references/migration-guide.md +19 -5
- package/assets/init/.qfai/assistant/skill/qfai-prototyping/SKILL.md +1 -1
- package/assets/init/.qfai/assistant/skill/qfai-prototyping/references/design-md-spec.md +14 -5
- package/assets/init/.qfai/assistant/skill/qfai-prototyping/references/generator-prompt.md +10 -0
- package/assets/init/.qfai/assistant/skill/qfai-prototyping/references/handoff.md +1 -1
- package/assets/init/.qfai/assistant/skill/qfai-prototyping/references/iteration-loop.md +3 -4
- package/assets/init/.qfai/assistant/skill/qfai-prototyping/references/review-payload-schema.md +10 -2
- package/assets/init/.qfai/assistant/skill/qfai-prototyping/references/reviewer-prompt.md +16 -3
- package/assets/init/.qfai/assistant/skill/qfai-run/SKILL.md +6 -21
- package/assets/init/.qfai/assistant/skill/qfai-run/references/extraction.md +30 -10
- package/assets/init/.qfai/assistant/skill/qfai-run/references/operator-screens.md +34 -0
- package/assets/init/.qfai/assistant/skill/qfai-run/references/plan.md +28 -5
- package/assets/init/.qfai/assistant/skill/qfai-run/references/stage-points.md +29 -0
- package/assets/init/.qfai/assistant/skill/qfai-sdd/SKILL.md +1 -1
- package/assets/init/.qfai/assistant/skill/qfai-sdd/references/concrete-abstract-cycle.md +10 -1
- package/assets/init/.qfai/assistant/skill/qfai-sdd/references/contract-artifact-rules.md +21 -9
- package/assets/init/.qfai/assistant/skill/qfai-sdd/references/design-anti-patterns.md +19 -2
- package/assets/init/.qfai/assistant/skill/qfai-sdd/references/design-md-authoring.md +11 -5
- package/assets/init/.qfai/assistant/skill/qfai-sdd/references/design-md-brand-catalog.md +17 -2
- package/assets/init/.qfai/assistant/skill/qfai-sdd/references/requirements-decomposition.md +2 -2
- package/assets/init/.qfai/assistant/skill/qfai-sdd/references/sdd-execution-playbook.md +5 -5
- package/assets/init/.qfai/assistant/skill/qfai-sdd/references/sdd-phase-checklists.md +4 -4
- package/assets/init/.qfai/assistant/skill/qfai-sdd/references/sdd-quality-gate.md +2 -2
- package/assets/init/.qfai/assistant/skill/qfai-sdd/references/sdd-triage.md +47 -5
- package/assets/init/.qfai/assistant/skill/qfai-sdd/references/spec-traceability-rules.md +15 -3
- package/assets/init/.qfai/assistant/skill/qfai-sdd/references/ui-contract-guide.md +16 -1
- package/assets/init/.qfai/assistant/skill/qfai-sdd/references/ui-design-contract-normalization.md +1 -2
- package/assets/init/.qfai/assistant/skill/qfai-sdd/templates/change-request.md +2 -2
- package/assets/init/.qfai/assistant/skill/qfai-sdd/templates/contracts/db-contract.sample.sql +2 -2
- package/assets/init/.qfai/assistant/skill/qfai-sdd/templates/spec/03_contract/cli/cli-NNNN-title.md +2 -2
- package/assets/init/.qfai/assistant/skill/qfai-sdd/templates/spec/03_contract/tech.md +1 -1
- package/assets/init/.qfai/assistant/skill/qfai-sdd/templates/spec/decisions.md +4 -0
- package/assets/init/.qfai/assistant/skill/qfai-triage/SKILL.md +1 -1
- package/assets/init/.qfai/assistant/skill/qfai-verify/SKILL.md +1 -1
- package/assets/init/.qfai/assistant/skill/qfai-verify/references/verify-output-contract.md +22 -8
- package/assets/init/.qfai/assistant/skill/web-research/SKILL.md +2 -10
- package/assets/init/.qfai/assistant/step/common-design-md/STEP.md +17 -3
- package/assets/init/.qfai/assistant/step/common-gate-run/STEP.md +30 -7
- package/assets/init/.qfai/assistant/step/discussion-uiux/STEP.md +11 -1
- package/assets/init/.qfai/assistant/step/implement-dep-bump/STEP.md +7 -0
- package/assets/init/.qfai/assistant/step/implement-scaffold/STEP.md +3 -3
- package/assets/init/.qfai/assistant/step/implement-seam/STEP.md +3 -1
- package/assets/init/.qfai/assistant/step/implement-tdd/STEP.md +3 -1
- package/assets/init/.qfai/assistant/step/implement-test-fix/STEP.md +2 -0
- package/assets/init/.qfai/assistant/step/sdd-flow/STEP.md +2 -0
- package/assets/init/.qfai/assistant/step/sdd-gate/STEP.md +4 -0
- package/assets/init/.qfai/assistant/step/sdd-triage/STEP.md +7 -3
- package/assets/init/.qfai/assistant/step/verify-change-note/STEP.md +8 -5
- package/assets/init/.qfai/assistant/step/verify-commit/STEP.md +6 -1
- package/assets/init/.qfai/assistant/step/verify-qfai-gate/STEP.md +20 -3
- package/assets/init/.qfai/assistant/step/verify-repeat-run/STEP.md +1 -1
- package/assets/init/.qfai/assistant/step/verify-repo-gate/STEP.md +23 -6
- package/assets/init/.qfai/waivers.yml +1 -1
- package/assets/init/root/.agents/rules/ai-readable-markdown.md +98 -0
- package/assets/init/root/.agents/rules/api-budget.md +4 -5
- package/assets/init/root/.agents/rules/documentation-clarity.md +30 -2
- package/assets/init/root/.agents/rules/grilling.md +4 -4
- package/assets/init/root/.agents/rules/interface-clarity.md +5 -5
- package/assets/init/root/.agents/rules/minimal-implementation.md +8 -0
- package/assets/init/root/.agents/rules/reminders.json +2 -2
- package/assets/init/root/.agents/rules/temporary-files.md +14 -5
- package/assets/init/root/.agents/rules/untrusted-content.md +3 -3
- package/assets/init/root/.agents/rules/user-questions.md +34 -7
- package/assets/init/root/.gitattributes +9 -0
- package/assets/init/root/.github/workflows/qfai-docs.yml +43 -11
- package/assets/init/root/.github/workflows/qfai-tests.yml +58 -15
- package/assets/init/root/.github/workflows/qfai-validate.yml +49 -42
- package/assets/init/root/AGENTS.md +1 -0
- package/assets/init/root/CLAUDE.md +1 -0
- package/assets/mdschema/story/03_contract/tech.mdschema.yml +7 -3
- package/assets/sandbox-templates/default-deny.yaml +0 -2
- package/assets/schemas/workflow/plan.schema.json +9 -2
- package/assets/scripts/check-mdschema.mjs +172 -5
- package/dist/cli/index.cjs +3979 -3055
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.mjs +3167 -2243
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.cjs +3024 -2500
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -9
- package/dist/index.d.ts +23 -9
- package/dist/index.mjs +2881 -2358
- package/dist/index.mjs.map +1 -1
- package/docs/MIGRATION-2.0.0.md +1 -1
- package/package.json +3 -3
|
@@ -85,6 +85,7 @@
|
|
|
85
85
|
* node scripts/check-mdschema.mjs --scope all --summary
|
|
86
86
|
* node scripts/check-mdschema.mjs --base <ref> # ratchet against <ref>
|
|
87
87
|
* node scripts/check-mdschema.mjs --scope files a.md b.md
|
|
88
|
+
* node scripts/check-mdschema.mjs --scope all --show-patterns # the pattern behind each forbidden-text finding
|
|
88
89
|
* node scripts/check-mdschema.mjs --root <dir> --scope all # another tree
|
|
89
90
|
* node scripts/check-mdschema.mjs --tools <dir> --scope all # mdschema installed under <dir>
|
|
90
91
|
*
|
|
@@ -223,6 +224,27 @@ function mdschemaEntryPoint(packageDir) {
|
|
|
223
224
|
return existsSync(entry) ? entry : null;
|
|
224
225
|
}
|
|
225
226
|
|
|
227
|
+
/**
|
|
228
|
+
* The text of a double-quoted YAML scalar, with its escapes decoded the way the
|
|
229
|
+
* configuration loader's YAML parser decodes them, so `"docs\tree"` names the
|
|
230
|
+
* directory the loader reads and not `docs/tree`.
|
|
231
|
+
*
|
|
232
|
+
* SIMPLIFIED: decodes the escapes JSON shares with YAML and keeps any other
|
|
233
|
+
* escape as written.
|
|
234
|
+
* Lift when: a configuration spells a path with an escape only YAML has, such
|
|
235
|
+
* as `\x41` or `\_`.
|
|
236
|
+
*
|
|
237
|
+
* @param {string} body The text between the quotes.
|
|
238
|
+
* @returns {string}
|
|
239
|
+
*/
|
|
240
|
+
function decodeDoubleQuoted(body) {
|
|
241
|
+
try {
|
|
242
|
+
return JSON.parse(`"${body}"`);
|
|
243
|
+
} catch {
|
|
244
|
+
return body;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
226
248
|
/**
|
|
227
249
|
* Reads one directory under `paths` out of `qfai.config.yaml`.
|
|
228
250
|
*
|
|
@@ -255,13 +277,16 @@ function readConfiguredDir(root, key, fallback) {
|
|
|
255
277
|
// to the block so an unrelated key under another mapping cannot win.
|
|
256
278
|
const block = /^paths:[ \t]*$([\s\S]*?)^(?=\S)/m.exec(`${text}\n`);
|
|
257
279
|
const scope = block === null ? text : block[1];
|
|
258
|
-
const found = new RegExp(
|
|
259
|
-
|
|
260
|
-
|
|
280
|
+
const found = new RegExp(
|
|
281
|
+
`^[ \\t]+${key}:[ \\t]*(?:"((?:[^"\\\\\\r\\n]|\\\\.)*)"|'([^'\\r\\n]*)'|([^"'\\r\\n#]+))[ \\t]*$`,
|
|
282
|
+
"m",
|
|
283
|
+
).exec(scope);
|
|
261
284
|
if (found === null) {
|
|
262
285
|
return fallback;
|
|
263
286
|
}
|
|
264
|
-
const value =
|
|
287
|
+
const value = (
|
|
288
|
+
found[1] === undefined ? (found[2] ?? found[3]) : decodeDoubleQuoted(found[1])
|
|
289
|
+
).trim();
|
|
265
290
|
if (value === "") {
|
|
266
291
|
return fallback;
|
|
267
292
|
}
|
|
@@ -726,10 +751,11 @@ function runMdschema(mdschema, schemaPath, files, root) {
|
|
|
726
751
|
output: result.error instanceof Error ? result.error.message : "mdschema did not run",
|
|
727
752
|
};
|
|
728
753
|
}
|
|
754
|
+
const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trimEnd();
|
|
729
755
|
return {
|
|
730
756
|
ok: result.status === 0,
|
|
731
757
|
spawnFailed: false,
|
|
732
|
-
output:
|
|
758
|
+
output: showPatterns ? output : explainForbiddenText(output, readFileSync(schemaPath, "utf-8")),
|
|
733
759
|
};
|
|
734
760
|
}
|
|
735
761
|
|
|
@@ -877,6 +903,143 @@ function documentUniverse(root, paths) {
|
|
|
877
903
|
];
|
|
878
904
|
}
|
|
879
905
|
|
|
906
|
+
/**
|
|
907
|
+
* Whether a finding names the pattern that failed, as `mdschema` prints it,
|
|
908
|
+
* rather than the rule the pattern enforces. Set by `--show-patterns`, for the
|
|
909
|
+
* author of a schema.
|
|
910
|
+
*/
|
|
911
|
+
let showPatterns = false;
|
|
912
|
+
|
|
913
|
+
/** A forbidden-text finding: the pattern that matched and the section it matched in. */
|
|
914
|
+
const FORBIDDEN_TEXT_MESSAGE = /^Forbidden text '.*' found in section '([^']*)'$/u;
|
|
915
|
+
|
|
916
|
+
/**
|
|
917
|
+
* The heading entries of a schema with the description each declares.
|
|
918
|
+
*
|
|
919
|
+
* Read with a line scanner, for the reason {@link rootHeadingPattern} is: the
|
|
920
|
+
* schemas are fixed shapes authored here, and this script runs before anything
|
|
921
|
+
* beyond the root devDependencies is installed. A description is a plain value
|
|
922
|
+
* or a folded or literal block.
|
|
923
|
+
*
|
|
924
|
+
* @param {string} schemaText
|
|
925
|
+
* @returns {{ pattern: string, regex: boolean, description: string }[]}
|
|
926
|
+
*/
|
|
927
|
+
export function sectionDescriptions(schemaText) {
|
|
928
|
+
const lines = schemaText.split(/\r?\n/);
|
|
929
|
+
const entries = [];
|
|
930
|
+
let current = null;
|
|
931
|
+
for (let i = 0; i < lines.length; i++) {
|
|
932
|
+
const raw = lines[i];
|
|
933
|
+
if (/^\s*-\s+heading:\s*$/.test(raw)) {
|
|
934
|
+
current = { pattern: "", regex: false, description: "" };
|
|
935
|
+
entries.push(current);
|
|
936
|
+
continue;
|
|
937
|
+
}
|
|
938
|
+
if (current === null) continue;
|
|
939
|
+
const pattern = /^\s+pattern:\s*(?:"([^"]*)"|'([^']*)'|(\S+))\s*$/.exec(raw);
|
|
940
|
+
if (pattern !== null && current.pattern === "") {
|
|
941
|
+
current.pattern = pattern[1] ?? pattern[2] ?? pattern[3] ?? "";
|
|
942
|
+
current.regex = /^\s+regex:\s*true\s*$/.test(lines[i + 1] ?? "");
|
|
943
|
+
continue;
|
|
944
|
+
}
|
|
945
|
+
const description = /^(\s*)description:\s*(.*?)\s*$/.exec(raw);
|
|
946
|
+
if (description === null || current.description !== "") continue;
|
|
947
|
+
const value = description[2];
|
|
948
|
+
if (/^[>|][+-]?$/.test(value)) {
|
|
949
|
+
const indent = description[1].length;
|
|
950
|
+
const block = [];
|
|
951
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
952
|
+
const next = lines[j];
|
|
953
|
+
if (next.trim() !== "" && next.length - next.trimStart().length <= indent) break;
|
|
954
|
+
block.push(next.trim());
|
|
955
|
+
}
|
|
956
|
+
current.description = block.join(" ").trim();
|
|
957
|
+
} else {
|
|
958
|
+
current.description = value.replace(/^(["'])(.*)\1$/u, "$2");
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
return entries;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
/**
|
|
965
|
+
* What the schema says the section named `name` holds, or `null`.
|
|
966
|
+
*
|
|
967
|
+
* @param {{ pattern: string, regex: boolean, description: string }[]} entries
|
|
968
|
+
* @param {string} name the heading text, without its `#` marks
|
|
969
|
+
* @returns {string | null}
|
|
970
|
+
*/
|
|
971
|
+
function sectionDescription(entries, name) {
|
|
972
|
+
for (const entry of entries) {
|
|
973
|
+
if (entry.description === "") continue;
|
|
974
|
+
for (let level = 1; level <= 6; level++) {
|
|
975
|
+
const heading = `${"#".repeat(level)} ${name}`;
|
|
976
|
+
let matches;
|
|
977
|
+
try {
|
|
978
|
+
matches = entry.regex
|
|
979
|
+
? new RegExp(entry.pattern, "u").test(heading)
|
|
980
|
+
: entry.pattern === heading;
|
|
981
|
+
} catch {
|
|
982
|
+
matches = false;
|
|
983
|
+
}
|
|
984
|
+
if (matches) return entry.description;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
return null;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
/**
|
|
991
|
+
* Rewrites the forbidden-text findings in one batch of `mdschema check` output
|
|
992
|
+
* so each says what its section holds, in the schema's own words, instead of
|
|
993
|
+
* the pattern that failed.
|
|
994
|
+
*
|
|
995
|
+
* Patterns that fail at one position of one document are one finding: the
|
|
996
|
+
* reader fixes a line, not a pattern. A finding in a section whose schema gives
|
|
997
|
+
* no description keeps the pattern, because it is all there is to say. The
|
|
998
|
+
* closing tally follows the findings that remain.
|
|
999
|
+
*
|
|
1000
|
+
* @param {string} output
|
|
1001
|
+
* @param {string} schemaText
|
|
1002
|
+
* @returns {string}
|
|
1003
|
+
*/
|
|
1004
|
+
export function explainForbiddenText(output, schemaText) {
|
|
1005
|
+
const entries = sectionDescriptions(schemaText);
|
|
1006
|
+
const lines = [];
|
|
1007
|
+
let seen = new Set();
|
|
1008
|
+
let merged = 0;
|
|
1009
|
+
for (const line of output.split(/\r?\n/)) {
|
|
1010
|
+
if (/^\S/.test(line)) seen = new Set();
|
|
1011
|
+
const found = VIOLATION_LINE.exec(line);
|
|
1012
|
+
const forbidden =
|
|
1013
|
+
found !== null && found[3] === "forbidden-text"
|
|
1014
|
+
? FORBIDDEN_TEXT_MESSAGE.exec(found[4])
|
|
1015
|
+
: null;
|
|
1016
|
+
const description = forbidden === null ? null : sectionDescription(entries, forbidden[1]);
|
|
1017
|
+
if (found === null || forbidden === null || description === null) {
|
|
1018
|
+
lines.push(line);
|
|
1019
|
+
continue;
|
|
1020
|
+
}
|
|
1021
|
+
const key = `${found[1]}:${found[2]}:${forbidden[1]}`;
|
|
1022
|
+
if (seen.has(key)) {
|
|
1023
|
+
merged++;
|
|
1024
|
+
continue;
|
|
1025
|
+
}
|
|
1026
|
+
seen.add(key);
|
|
1027
|
+
lines.push(
|
|
1028
|
+
` ✗ ${found[1]}:${found[2]} [forbidden-text] Section '${forbidden[1]}' does not hold what the schema requires: ${description}`,
|
|
1029
|
+
);
|
|
1030
|
+
}
|
|
1031
|
+
return lines
|
|
1032
|
+
.map((line) =>
|
|
1033
|
+
merged === 0
|
|
1034
|
+
? line
|
|
1035
|
+
: line.replace(
|
|
1036
|
+
/^(\S+\s+Found )(\d+)( violation)/u,
|
|
1037
|
+
(_all, head, count, tail) => `${head}${Number(count) - merged}${tail}`,
|
|
1038
|
+
),
|
|
1039
|
+
)
|
|
1040
|
+
.join("\n");
|
|
1041
|
+
}
|
|
1042
|
+
|
|
880
1043
|
/** One violation line of `mdschema check` output: ` ✗ 3:4 [rule] message`. */
|
|
881
1044
|
const VIOLATION_LINE = /^\s+✗\s+(\d+):(\d+)\s+\[([^\]]+)\]\s+(.*\S)\s*$/u;
|
|
882
1045
|
|
|
@@ -1056,6 +1219,10 @@ function parseArgs(argv) {
|
|
|
1056
1219
|
summary = true;
|
|
1057
1220
|
continue;
|
|
1058
1221
|
}
|
|
1222
|
+
if (arg === "--show-patterns") {
|
|
1223
|
+
showPatterns = true;
|
|
1224
|
+
continue;
|
|
1225
|
+
}
|
|
1059
1226
|
if (arg.startsWith("-")) {
|
|
1060
1227
|
console.error(`check-mdschema: unknown flag ${arg}`);
|
|
1061
1228
|
return 2;
|