qfai 1.8.2 → 1.8.4

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.
Files changed (39) hide show
  1. package/README.md +9 -4
  2. package/assets/init/.qfai/assistant/agents/product-experience-architect.md +2 -1
  3. package/assets/init/.qfai/assistant/skills/qfai-atdd/SKILL.md +4 -4
  4. package/assets/init/.qfai/assistant/skills/qfai-configure/SKILL.md +1 -1
  5. package/assets/init/.qfai/assistant/skills/qfai-discussion/SKILL.md +1 -0
  6. package/assets/init/.qfai/assistant/skills/qfai-discussion/references/rcp_footer.md +1 -1
  7. package/assets/init/.qfai/assistant/skills/qfai-implement/SKILL.md +3 -1
  8. package/assets/init/.qfai/assistant/skills/qfai-prototyping/SKILL.md +126 -62
  9. package/assets/init/.qfai/assistant/skills/qfai-prototyping/references/evidence-requirements.md +43 -12
  10. package/assets/init/.qfai/assistant/skills/qfai-prototyping/references/iteration-cycle.md +46 -14
  11. package/assets/init/.qfai/assistant/skills/qfai-prototyping/references/l1-review-guide.md +13 -12
  12. package/assets/init/.qfai/assistant/skills/qfai-prototyping/references/l2-review-guide.md +16 -10
  13. package/assets/init/.qfai/assistant/skills/qfai-prototyping/references/reviewer-gate.md +25 -4
  14. package/assets/init/.qfai/assistant/skills/qfai-sdd/SKILL.md +3 -3
  15. package/assets/init/.qfai/assistant/skills/qfai-sdd/references/rcp_footer.md +1 -1
  16. package/assets/init/.qfai/assistant/skills/qfai-sdd/references/sdd-quality-gate.md +1 -1
  17. package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/contracts/absorption-policy.sample.yaml +7 -0
  18. package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/contracts/evaluation-rubric.sample.yaml +22 -3
  19. package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/contracts/evaluator-calibration.sample.yaml +6 -0
  20. package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/contracts/exploration-brief.sample.yaml +9 -0
  21. package/assets/init/.qfai/assistant/skills/qfai-verify/SKILL.md +6 -6
  22. package/assets/init/.qfai/contracts/design/README.md +6 -1
  23. package/assets/init/.qfai/contracts/ui/README.md +3 -3
  24. package/assets/init/.qfai/discussion/README.md +14 -9
  25. package/assets/init/.qfai/evidence/README.md +66 -46
  26. package/assets/init/root/.github/workflows/qfai-validate.yml +39 -0
  27. package/assets/init/root/qfai.config.yaml +1 -2
  28. package/dist/cli/index.cjs +8787 -5641
  29. package/dist/cli/index.cjs.map +1 -1
  30. package/dist/cli/index.mjs +5993 -2847
  31. package/dist/cli/index.mjs.map +1 -1
  32. package/dist/index.cjs +2677 -702
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.d.cts +102 -23
  35. package/dist/index.d.ts +102 -23
  36. package/dist/index.mjs +2651 -675
  37. package/dist/index.mjs.map +1 -1
  38. package/package.json +4 -2
  39. package/assets/scripts/capture-screenshots.js +0 -128
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qfai",
3
- "version": "1.8.2",
3
+ "version": "1.8.4",
4
4
  "description": "Quality-first AI-driven development toolkit (SDD × ATDD × TDD).",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -36,6 +36,7 @@
36
36
  "build": "tsup",
37
37
  "prepack": "npm run build",
38
38
  "lint": "eslint src",
39
+ "lint:shipping": "vitest run tests/scripts/lintShipping.test.ts",
39
40
  "check-types": "tsc --noEmit",
40
41
  "test": "vitest run",
41
42
  "test:core": "vitest run --project core",
@@ -43,7 +44,8 @@
43
44
  "test:integration": "vitest run --project integration",
44
45
  "test:e2e": "vitest run --project e2e",
45
46
  "test:cli": "vitest run --project cli",
46
- "test:assets": "vitest run tests/assets"
47
+ "test:assets": "vitest run tests/assets",
48
+ "self-validate": "node dist/cli/index.mjs validate --profile tdd --fail-on error --root ../.."
47
49
  },
48
50
  "dependencies": {
49
51
  "@cucumber/gherkin": "^37.0.1",
@@ -1,128 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * capture-screenshots.js — Headless screenshot capture utility for qfai-prototyping.
4
- *
5
- * Usage:
6
- * node capture-screenshots.js --url <url> --out <dir> [--width <px>] [--height <px>]
7
- *
8
- * Outputs:
9
- * <dir>/screenshot-<ISOtimestamp>.png — full-page screenshot
10
- * <dir>/manifest.json — { url, capturedAt, files: [{path, timestamp}] }
11
- *
12
- * Exit codes:
13
- * 0 — success (at least one screenshot captured)
14
- * 1 — argument error (missing --url or --out)
15
- * 2 — capture error (unreachable URL or write failure)
16
- *
17
- * Input/output contract:
18
- * Input : --url (string, required) — the page URL to capture
19
- * --out (string, required) — directory to write screenshots; created if absent
20
- * --width (number, default 1280) — viewport width in px
21
- * --height (number, default 800) — viewport height in px
22
- *
23
- * Output: each screenshot filename includes an ISO-8601-like timestamp suffix
24
- * (e.g. screenshot-2026-04-18T130000123Z.png) so filenames are
25
- * sortable and unique per run.
26
- *
27
- * spec-0012 TC-0012-0291 / AC-0012-0176
28
- */
29
-
30
- "use strict";
31
-
32
- import fs from "node:fs";
33
- import path from "node:path";
34
- import { execSync } from "node:child_process";
35
-
36
- function parseArgs(argv) {
37
- const args = {};
38
- for (let i = 0; i < argv.length; i++) {
39
- if (argv[i] === "--url" && argv[i + 1]) {
40
- args.url = argv[++i];
41
- } else if (argv[i] === "--out" && argv[i + 1]) {
42
- args.out = argv[++i];
43
- } else if (argv[i] === "--width" && argv[i + 1]) {
44
- args.width = parseInt(argv[++i], 10);
45
- } else if (argv[i] === "--height" && argv[i + 1]) {
46
- args.height = parseInt(argv[++i], 10);
47
- }
48
- }
49
- return args;
50
- }
51
-
52
- function isoTimestamp() {
53
- return new Date().toISOString().replace(/[:.]/g, "").replace("T", "T").slice(0, -1) + "Z";
54
- }
55
-
56
- function main() {
57
- const args = parseArgs(process.argv.slice(2));
58
-
59
- if (!args.url) {
60
- console.error("Error: --url is required");
61
- process.exit(1);
62
- }
63
- if (!args.out) {
64
- console.error("Error: --out is required");
65
- process.exit(1);
66
- }
67
-
68
- const outDir = path.resolve(args.out);
69
- fs.mkdirSync(outDir, { recursive: true });
70
-
71
- const timestamp = isoTimestamp();
72
- const filename = `screenshot-${timestamp}.png`;
73
- const screenshotPath = path.join(outDir, filename);
74
-
75
- let captured = false;
76
- let captureError = null;
77
- try {
78
- // Try puppeteer-based capture (available when installed). Any failure
79
- // must surface as a capture error; writing fake PNG bytes would corrupt
80
- // the evidence chain by making downstream steps think capture succeeded.
81
- const puppeteerScript = `
82
- const puppeteer = require('puppeteer');
83
- (async () => {
84
- const browser = await puppeteer.launch({ args: ['--no-sandbox'] });
85
- const page = await browser.newPage();
86
- await page.setViewport({ width: ${args.width || 1280}, height: ${args.height || 800} });
87
- await page.goto(${JSON.stringify(args.url)}, { waitUntil: 'networkidle0', timeout: 30000 });
88
- await page.screenshot({ path: ${JSON.stringify(screenshotPath)}, fullPage: true });
89
- await browser.close();
90
- })();
91
- `;
92
- const tmpScript = path.join(outDir, `_tmp_capture_${timestamp}.js`);
93
- fs.writeFileSync(tmpScript, puppeteerScript, "utf-8");
94
- try {
95
- execSync(`node "${tmpScript}"`, { timeout: 60000, stdio: "pipe" });
96
- captured = true;
97
- } finally {
98
- try {
99
- fs.unlinkSync(tmpScript);
100
- } catch {
101
- /* ignore */
102
- }
103
- }
104
- } catch (error) {
105
- captureError = error;
106
- }
107
-
108
- if (!captured) {
109
- const detail =
110
- captureError && typeof captureError.message === "string"
111
- ? captureError.message
112
- : String(captureError || "unknown error");
113
- console.error(`Error: capture failed (${detail})`);
114
- process.exit(2);
115
- }
116
-
117
- const manifest = {
118
- url: args.url,
119
- capturedAt: new Date().toISOString(),
120
- files: [{ path: screenshotPath, timestamp }],
121
- };
122
- fs.writeFileSync(path.join(outDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
123
-
124
- console.log(screenshotPath);
125
- process.exit(0);
126
- }
127
-
128
- main();