qfai 1.2.14 → 1.3.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/assets/init/.qfai/assistant/instructions/workflow.md +16 -0
- package/assets/init/.qfai/assistant/prompts/qfai-spec.md +12 -0
- package/assets/init/.qfai/assistant/prompts/qfai-tdd-green.md +12 -0
- package/assets/init/.qfai/assistant/prompts/qfai-tdd-red.md +12 -0
- package/assets/init/.qfai/assistant/prompts/qfai-tdd-refactor.md +12 -0
- package/assets/init/.qfai/specs/README.md +5 -0
- package/dist/cli/index.cjs +184 -6
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.mjs +184 -6
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.cjs +184 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +184 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -14,6 +14,22 @@ This file defines the canonical stages and delegation expectations.
|
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
+
## Change Type (Mandatory)
|
|
18
|
+
|
|
19
|
+
At the start of any work, classify the change and record it in:
|
|
20
|
+
|
|
21
|
+
- `delta.md` Change Log (latest CL entry)
|
|
22
|
+
- PR description (Change Type section)
|
|
23
|
+
|
|
24
|
+
Allowed values:
|
|
25
|
+
|
|
26
|
+
- Primary: `Initial | Behavior | Structural | Ops`
|
|
27
|
+
- Tags (optional): `@ui @api @db @nfr @docs @test`
|
|
28
|
+
|
|
29
|
+
Do not proceed without a declared Change Type.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
17
33
|
## Stages (canonical)
|
|
18
34
|
|
|
19
35
|
0. Steering refresh (project memory bootstrap)
|
|
@@ -51,6 +51,18 @@ When unsure, read inputs in this order:
|
|
|
51
51
|
- P3: `.qfai/specs/<spec-id>/delta.md` (Decision Records; if no spec yet, state "not applicable")
|
|
52
52
|
- P4: other artifacts (spec.md, scenario.feature, contracts, evidence)
|
|
53
53
|
|
|
54
|
+
## Change Type (Mandatory)
|
|
55
|
+
|
|
56
|
+
Before editing `delta.md`, declare the Change Type and record it in:
|
|
57
|
+
|
|
58
|
+
- `delta.md` Change Log (latest CL entry)
|
|
59
|
+
- PR description (Change Type section)
|
|
60
|
+
|
|
61
|
+
Allowed values:
|
|
62
|
+
|
|
63
|
+
- Primary: `Initial | Behavior | Structural | Ops`
|
|
64
|
+
- Tags (optional): `@ui @api @db @nfr @docs @test`
|
|
65
|
+
|
|
54
66
|
## Delta Rejected Guard (Mandatory)
|
|
55
67
|
|
|
56
68
|
- Do NOT reintroduce options marked as rejected in delta.md.
|
|
@@ -38,6 +38,18 @@ When unsure, read inputs in this order:
|
|
|
38
38
|
- P3: `.qfai/specs/<spec-id>/delta.md` (Decision Records; if no spec yet, state "not applicable")
|
|
39
39
|
- P4: other artifacts (spec.md, scenario.feature, contracts, evidence)
|
|
40
40
|
|
|
41
|
+
## Change Type (Mandatory)
|
|
42
|
+
|
|
43
|
+
Before updating `delta.md`, declare the Change Type and record it in:
|
|
44
|
+
|
|
45
|
+
- `delta.md` Change Log (latest CL entry)
|
|
46
|
+
- PR description (Change Type section)
|
|
47
|
+
|
|
48
|
+
Allowed values:
|
|
49
|
+
|
|
50
|
+
- Primary: `Initial | Behavior | Structural | Ops`
|
|
51
|
+
- Tags (optional): `@ui @api @db @nfr @docs @test`
|
|
52
|
+
|
|
41
53
|
## Delta Rejected Guard (Mandatory)
|
|
42
54
|
|
|
43
55
|
- Do NOT reintroduce options marked as rejected in delta.md.
|
|
@@ -38,6 +38,18 @@ When unsure, read inputs in this order:
|
|
|
38
38
|
- P3: `.qfai/specs/<spec-id>/delta.md` (Decision Records; if no spec yet, state "not applicable")
|
|
39
39
|
- P4: other artifacts (spec.md, scenario.feature, contracts, evidence)
|
|
40
40
|
|
|
41
|
+
## Change Type (Mandatory)
|
|
42
|
+
|
|
43
|
+
Before updating `delta.md`, declare the Change Type and record it in:
|
|
44
|
+
|
|
45
|
+
- `delta.md` Change Log (latest CL entry)
|
|
46
|
+
- PR description (Change Type section)
|
|
47
|
+
|
|
48
|
+
Allowed values:
|
|
49
|
+
|
|
50
|
+
- Primary: `Initial | Behavior | Structural | Ops`
|
|
51
|
+
- Tags (optional): `@ui @api @db @nfr @docs @test`
|
|
52
|
+
|
|
41
53
|
## Delta Rejected Guard (Mandatory)
|
|
42
54
|
|
|
43
55
|
- Do NOT reintroduce options marked as rejected in delta.md.
|
|
@@ -38,6 +38,18 @@ When unsure, read inputs in this order:
|
|
|
38
38
|
- P3: `.qfai/specs/<spec-id>/delta.md` (Decision Records; if no spec yet, state "not applicable")
|
|
39
39
|
- P4: other artifacts (spec.md, scenario.feature, contracts, evidence)
|
|
40
40
|
|
|
41
|
+
## Change Type (Mandatory)
|
|
42
|
+
|
|
43
|
+
Before updating `delta.md`, declare the Change Type and record it in:
|
|
44
|
+
|
|
45
|
+
- `delta.md` Change Log (latest CL entry)
|
|
46
|
+
- PR description (Change Type section)
|
|
47
|
+
|
|
48
|
+
Allowed values:
|
|
49
|
+
|
|
50
|
+
- Primary: `Initial | Behavior | Structural | Ops`
|
|
51
|
+
- Tags (optional): `@ui @api @db @nfr @docs @test`
|
|
52
|
+
|
|
41
53
|
## Delta Rejected Guard (Mandatory)
|
|
42
54
|
|
|
43
55
|
- Do NOT reintroduce options marked as rejected in delta.md.
|
|
@@ -185,6 +185,9 @@ specs/
|
|
|
185
185
|
|
|
186
186
|
- date: <YYYY-MM-DD>
|
|
187
187
|
- author: <AI/role or human>
|
|
188
|
+
- change_type_primary: Initial | Behavior | Structural | Ops
|
|
189
|
+
- change_type_tags: <space-separated tags or empty>
|
|
190
|
+
- example: @api @db
|
|
188
191
|
- scope: <files/areas>
|
|
189
192
|
- change: <what changed>
|
|
190
193
|
- reason: <why it changed>
|
|
@@ -204,6 +207,8 @@ specs/
|
|
|
204
207
|
- selected: <option>
|
|
205
208
|
- rejected:
|
|
206
209
|
- <option> — <reason>
|
|
210
|
+
- do_not: <what must not be reintroduced>
|
|
211
|
+
- temptation: <why it may be tempting to reintroduce>
|
|
207
212
|
- impact: <downstream impact>
|
|
208
213
|
- followups: <todos>
|
|
209
214
|
- related_contracts: <QFAI-CONTRACT-REF or IDs>
|
package/dist/cli/index.cjs
CHANGED
|
@@ -1184,8 +1184,8 @@ var import_promises7 = require("fs/promises");
|
|
|
1184
1184
|
var import_node_path9 = __toESM(require("path"), 1);
|
|
1185
1185
|
var import_node_url2 = require("url");
|
|
1186
1186
|
async function resolveToolVersion() {
|
|
1187
|
-
if ("1.
|
|
1188
|
-
return "1.
|
|
1187
|
+
if ("1.3.0".length > 0) {
|
|
1188
|
+
return "1.3.0";
|
|
1189
1189
|
}
|
|
1190
1190
|
try {
|
|
1191
1191
|
const packagePath = resolvePackageJsonPath();
|
|
@@ -3568,6 +3568,24 @@ async function validateCaseCatalogues(root, config) {
|
|
|
3568
3568
|
// src/core/validators/delta.ts
|
|
3569
3569
|
var import_promises15 = require("fs/promises");
|
|
3570
3570
|
var import_node_path19 = __toESM(require("path"), 1);
|
|
3571
|
+
var CHANGE_TYPE_PRIMARY_RE = /^\s*[-*]?\s*change_type_primary\s*:\s*(.+)\s*$/im;
|
|
3572
|
+
var CHANGE_TYPE_TAGS_RE = /^\s*[-*]?\s*change_type_tags\s*:\s*(.*)\s*$/im;
|
|
3573
|
+
var DO_NOT_RE = /^\s*[-*]?\s*do_not\s*:/im;
|
|
3574
|
+
var TEMPTATION_RE = /^\s*[-*]?\s*temptation\s*:/im;
|
|
3575
|
+
var ALLOWED_CHANGE_TYPE_PRIMARY = /* @__PURE__ */ new Set([
|
|
3576
|
+
"initial",
|
|
3577
|
+
"behavior",
|
|
3578
|
+
"structural",
|
|
3579
|
+
"ops"
|
|
3580
|
+
]);
|
|
3581
|
+
var ALLOWED_CHANGE_TYPE_TAGS = /* @__PURE__ */ new Set([
|
|
3582
|
+
"@ui",
|
|
3583
|
+
"@api",
|
|
3584
|
+
"@db",
|
|
3585
|
+
"@nfr",
|
|
3586
|
+
"@docs",
|
|
3587
|
+
"@test"
|
|
3588
|
+
]);
|
|
3571
3589
|
async function validateDeltas(root, config) {
|
|
3572
3590
|
const specsRoot = resolvePath(root, config, "specsDir");
|
|
3573
3591
|
const packs = await collectSpecPackDirs(specsRoot);
|
|
@@ -3629,10 +3647,8 @@ async function validateDeltas(root, config) {
|
|
|
3629
3647
|
)
|
|
3630
3648
|
);
|
|
3631
3649
|
} else {
|
|
3632
|
-
const
|
|
3633
|
-
|
|
3634
|
-
);
|
|
3635
|
-
if (!hasRejected) {
|
|
3650
|
+
const rejectedBlocks = extractRejectedBlocks(decisionRecords.body);
|
|
3651
|
+
if (rejectedBlocks.length === 0) {
|
|
3636
3652
|
issues.push(
|
|
3637
3653
|
issue(
|
|
3638
3654
|
"QFAI-DELTA-101",
|
|
@@ -3645,6 +3661,28 @@ async function validateDeltas(root, config) {
|
|
|
3645
3661
|
"rejected \u3092\u6700\u4F4E1\u4EF6\u8A18\u8F09\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
|
|
3646
3662
|
)
|
|
3647
3663
|
);
|
|
3664
|
+
} else {
|
|
3665
|
+
const hasDoNot = rejectedBlocks.some((block) => DO_NOT_RE.test(block));
|
|
3666
|
+
const hasTemptation = rejectedBlocks.some(
|
|
3667
|
+
(block) => TEMPTATION_RE.test(block)
|
|
3668
|
+
);
|
|
3669
|
+
if (!hasDoNot || !hasTemptation) {
|
|
3670
|
+
const missing = [];
|
|
3671
|
+
if (!hasDoNot) missing.push("do_not");
|
|
3672
|
+
if (!hasTemptation) missing.push("temptation");
|
|
3673
|
+
issues.push(
|
|
3674
|
+
issue(
|
|
3675
|
+
"QFAI-DELTA-204",
|
|
3676
|
+
`Decision Records \u306B ${missing.join(" / ")} \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002`,
|
|
3677
|
+
"warning",
|
|
3678
|
+
deltaPath,
|
|
3679
|
+
"delta.rejectedDetails",
|
|
3680
|
+
void 0,
|
|
3681
|
+
"change",
|
|
3682
|
+
"rejected \u306B\u306F do_not / temptation \u3092\u6700\u4F4E1\u4EF6\u542B\u3081\u3066\u304F\u3060\u3055\u3044\u3002"
|
|
3683
|
+
)
|
|
3684
|
+
);
|
|
3685
|
+
}
|
|
3648
3686
|
}
|
|
3649
3687
|
}
|
|
3650
3688
|
if (changeLog && decisionRecords) {
|
|
@@ -3663,6 +3701,54 @@ async function validateDeltas(root, config) {
|
|
|
3663
3701
|
);
|
|
3664
3702
|
}
|
|
3665
3703
|
}
|
|
3704
|
+
if (changeLog) {
|
|
3705
|
+
const blocks = extractChangeLogBlocks(text, changeLog);
|
|
3706
|
+
for (const block of blocks) {
|
|
3707
|
+
const primary = extractChangeTypePrimary(block);
|
|
3708
|
+
if (!primary) {
|
|
3709
|
+
issues.push(
|
|
3710
|
+
issue(
|
|
3711
|
+
"QFAI-DELTA-201",
|
|
3712
|
+
"Change Log \u306E CL \u30D6\u30ED\u30C3\u30AF\u306B change_type_primary \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002",
|
|
3713
|
+
"warning",
|
|
3714
|
+
deltaPath,
|
|
3715
|
+
"delta.changeTypePrimary",
|
|
3716
|
+
void 0,
|
|
3717
|
+
"change",
|
|
3718
|
+
"change_type_primary \u3092\u8FFD\u52A0\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
|
|
3719
|
+
)
|
|
3720
|
+
);
|
|
3721
|
+
} else if (!isAllowedChangeTypePrimary(primary)) {
|
|
3722
|
+
issues.push(
|
|
3723
|
+
issue(
|
|
3724
|
+
"QFAI-DELTA-202",
|
|
3725
|
+
`change_type_primary \u304C\u4E0D\u6B63\u3067\u3059: ${primary}`,
|
|
3726
|
+
"warning",
|
|
3727
|
+
deltaPath,
|
|
3728
|
+
"delta.changeTypePrimary",
|
|
3729
|
+
void 0,
|
|
3730
|
+
"change",
|
|
3731
|
+
"change_type_primary \u306F Initial | Behavior | Structural | Ops \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
|
|
3732
|
+
)
|
|
3733
|
+
);
|
|
3734
|
+
}
|
|
3735
|
+
const invalidTags = extractInvalidChangeTypeTags(block);
|
|
3736
|
+
if (invalidTags && invalidTags.length > 0) {
|
|
3737
|
+
issues.push(
|
|
3738
|
+
issue(
|
|
3739
|
+
"QFAI-DELTA-203",
|
|
3740
|
+
`change_type_tags \u306B\u7121\u52B9\u306A\u30BF\u30B0\u304C\u3042\u308A\u307E\u3059: ${invalidTags.join(", ")}`,
|
|
3741
|
+
"warning",
|
|
3742
|
+
deltaPath,
|
|
3743
|
+
"delta.changeTypeTags",
|
|
3744
|
+
void 0,
|
|
3745
|
+
"change",
|
|
3746
|
+
"change_type_tags \u306F @ui @api @db @nfr @docs @test \u306E\u307F\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
|
|
3747
|
+
)
|
|
3748
|
+
);
|
|
3749
|
+
}
|
|
3750
|
+
}
|
|
3751
|
+
}
|
|
3666
3752
|
}
|
|
3667
3753
|
return issues;
|
|
3668
3754
|
}
|
|
@@ -3675,6 +3761,98 @@ function findSection(sections, title) {
|
|
|
3675
3761
|
}
|
|
3676
3762
|
return null;
|
|
3677
3763
|
}
|
|
3764
|
+
function extractChangeLogBlocks(text, changeLog) {
|
|
3765
|
+
const lines = text.split(/\r?\n/);
|
|
3766
|
+
const headings = parseHeadings(text).filter(
|
|
3767
|
+
(heading) => heading.level === 3 && heading.line >= changeLog.startLine && heading.line <= changeLog.endLine
|
|
3768
|
+
);
|
|
3769
|
+
if (headings.length === 0) {
|
|
3770
|
+
return [changeLog.body];
|
|
3771
|
+
}
|
|
3772
|
+
const blocks = [];
|
|
3773
|
+
for (let i = 0; i < headings.length; i += 1) {
|
|
3774
|
+
const current = headings[i];
|
|
3775
|
+
if (!current) continue;
|
|
3776
|
+
const next = headings[i + 1];
|
|
3777
|
+
const startLine = current.line + 1;
|
|
3778
|
+
const endLine = Math.min(
|
|
3779
|
+
changeLog.endLine,
|
|
3780
|
+
(next?.line ?? changeLog.endLine + 1) - 1
|
|
3781
|
+
);
|
|
3782
|
+
if (startLine > endLine) {
|
|
3783
|
+
blocks.push("");
|
|
3784
|
+
continue;
|
|
3785
|
+
}
|
|
3786
|
+
blocks.push(lines.slice(startLine - 1, endLine).join("\n"));
|
|
3787
|
+
}
|
|
3788
|
+
return blocks;
|
|
3789
|
+
}
|
|
3790
|
+
function extractChangeTypePrimary(block) {
|
|
3791
|
+
const match = block.match(CHANGE_TYPE_PRIMARY_RE);
|
|
3792
|
+
const value = match?.[1]?.trim() ?? "";
|
|
3793
|
+
return value.length > 0 ? value : null;
|
|
3794
|
+
}
|
|
3795
|
+
function extractRejectedBlocks(sectionBody) {
|
|
3796
|
+
const lines = sectionBody.split(/\r?\n/);
|
|
3797
|
+
const blocks = [];
|
|
3798
|
+
let currentIndent = null;
|
|
3799
|
+
let buffer = [];
|
|
3800
|
+
const flush = () => {
|
|
3801
|
+
if (currentIndent === null) {
|
|
3802
|
+
return;
|
|
3803
|
+
}
|
|
3804
|
+
blocks.push(buffer.join("\n"));
|
|
3805
|
+
buffer = [];
|
|
3806
|
+
currentIndent = null;
|
|
3807
|
+
};
|
|
3808
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
3809
|
+
const line = lines[i] ?? "";
|
|
3810
|
+
const match = line.match(/^(\s*)(?:[-*]\s*)?rejected\s*:(.*)$/i);
|
|
3811
|
+
if (match) {
|
|
3812
|
+
flush();
|
|
3813
|
+
const inlineValue = (match[2] ?? "").trim();
|
|
3814
|
+
if (inlineValue.length > 0) {
|
|
3815
|
+
blocks.push(inlineValue);
|
|
3816
|
+
currentIndent = null;
|
|
3817
|
+
} else {
|
|
3818
|
+
currentIndent = (match[1] ?? "").length;
|
|
3819
|
+
}
|
|
3820
|
+
continue;
|
|
3821
|
+
}
|
|
3822
|
+
if (currentIndent === null) {
|
|
3823
|
+
continue;
|
|
3824
|
+
}
|
|
3825
|
+
const indentMatch = line.match(/^(\s*)/);
|
|
3826
|
+
const indent = (indentMatch?.[1] ?? "").length;
|
|
3827
|
+
if (line.trim().length > 0 && indent <= currentIndent) {
|
|
3828
|
+
flush();
|
|
3829
|
+
i -= 1;
|
|
3830
|
+
continue;
|
|
3831
|
+
}
|
|
3832
|
+
buffer.push(line);
|
|
3833
|
+
}
|
|
3834
|
+
flush();
|
|
3835
|
+
return blocks;
|
|
3836
|
+
}
|
|
3837
|
+
function extractInvalidChangeTypeTags(block) {
|
|
3838
|
+
const match = block.match(CHANGE_TYPE_TAGS_RE);
|
|
3839
|
+
if (!match) {
|
|
3840
|
+
return null;
|
|
3841
|
+
}
|
|
3842
|
+
const raw = match[1]?.trim() ?? "";
|
|
3843
|
+
if (raw.length === 0) {
|
|
3844
|
+
return [];
|
|
3845
|
+
}
|
|
3846
|
+
const tags = raw.split(/[\s,]+/).map((tag) => tag.trim()).filter((tag) => tag.length > 0);
|
|
3847
|
+
const invalid = tags.filter((tag) => !isAllowedChangeTypeTag(tag));
|
|
3848
|
+
return invalid;
|
|
3849
|
+
}
|
|
3850
|
+
function isAllowedChangeTypePrimary(value) {
|
|
3851
|
+
return ALLOWED_CHANGE_TYPE_PRIMARY.has(value.trim().toLowerCase());
|
|
3852
|
+
}
|
|
3853
|
+
function isAllowedChangeTypeTag(value) {
|
|
3854
|
+
return ALLOWED_CHANGE_TYPE_TAGS.has(value.trim().toLowerCase());
|
|
3855
|
+
}
|
|
3678
3856
|
|
|
3679
3857
|
// src/core/validators/ids.ts
|
|
3680
3858
|
var import_promises16 = require("fs/promises");
|