gennady 0.8.4-draft.2 → 0.8.4-draft.3

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 (146) hide show
  1. package/ai/directives/sdd-v2/root.directive.xml +36 -3
  2. package/ai/directives/sdd-v2/router.directive.xml +22 -1
  3. package/ai/kit/templates/sdd-v2/root.directive.hbs +11 -3
  4. package/ai/kit/templates/sdd-v2/router.directive.hbs +22 -1
  5. package/ai/skills/agent-inbox/SKILL.md +1 -1
  6. package/ai/skills/sdd/SKILL.md +5 -3
  7. package/ai/skills/sdd-audit/SKILL.md +5 -2
  8. package/ai/skills/sdd-check/SKILL.md +2 -2
  9. package/ai/skills/sdd-code-review/SKILL.md +4 -1
  10. package/ai/skills/sdd-critic/SKILL.md +5 -2
  11. package/ai/skills/sdd-execute/SKILL.md +5 -2
  12. package/ai/skills/sdd-reconcile/SKILL.md +5 -2
  13. package/ai/skills/sdd-scaffold/SKILL.md +5 -2
  14. package/dist/ai/directives/sdd-v2/root.directive.xml +36 -3
  15. package/dist/ai/directives/sdd-v2/router.directive.xml +22 -1
  16. package/dist/ai/kit/templates/sdd-v2/root.directive.hbs +11 -3
  17. package/dist/ai/kit/templates/sdd-v2/router.directive.hbs +22 -1
  18. package/dist/ai/skills/agent-inbox/SKILL.md +1 -1
  19. package/dist/ai/skills/sdd/SKILL.md +5 -3
  20. package/dist/ai/skills/sdd-audit/SKILL.md +5 -2
  21. package/dist/ai/skills/sdd-check/SKILL.md +2 -2
  22. package/dist/ai/skills/sdd-code-review/SKILL.md +4 -1
  23. package/dist/ai/skills/sdd-critic/SKILL.md +5 -2
  24. package/dist/ai/skills/sdd-execute/SKILL.md +5 -2
  25. package/dist/ai/skills/sdd-reconcile/SKILL.md +5 -2
  26. package/dist/ai/skills/sdd-scaffold/SKILL.md +5 -2
  27. package/dist/chunks/{classify-mr-stage.logic-BiCHTmPp.js → classify-mr-stage.logic-CazmQDwZ.js} +2 -2
  28. package/dist/chunks/{config-index-9VHWSDQD.js → config-index-DU8-FEjq.js} +1 -1
  29. package/dist/chunks/{devtools-b0DwO1x_.js → devtools-1JmXJjUG.js} +1 -1
  30. package/dist/chunks/{eval.cmd-CwNPaNJd.js → eval.cmd-CBANnOFG.js} +1 -1
  31. package/dist/chunks/{help-D5cXtdMI.js → help-DVloaFF5.js} +1 -1
  32. package/dist/chunks/{index-BVnJEutp.js → index-2xhpqJtd.js} +2 -2
  33. package/dist/chunks/{index-RR5OUxje.js → index-AxsLBUAx.js} +14 -14
  34. package/dist/chunks/{index-CUkeTGqM.js → index-B7sydB0W.js} +2 -2
  35. package/dist/chunks/{index-CMCn9ErF.js → index-BKxk4zOz.js} +2 -2
  36. package/dist/chunks/{index-Db13NlHh.js → index-BNMmX_Mv.js} +21 -21
  37. package/dist/chunks/{index-CmQO-RmM.js → index-BPzldG3a.js} +2 -2
  38. package/dist/chunks/{index-BOQl7F_Z.js → index-BQlFDvKW.js} +2 -2
  39. package/dist/chunks/{index-RzkvIfJQ.js → index-Baq7RL1L.js} +3 -3
  40. package/dist/chunks/index-BbPiwvY9.js +2 -0
  41. package/dist/chunks/{index-ClJLPEUB.js → index-BreGNLJ_.js} +1 -1
  42. package/dist/chunks/{index-DjSafAkN.js → index-BslPwGtM.js} +2 -2
  43. package/dist/chunks/{index-DiMFxS0z.js → index-BtCUN2fZ.js} +16 -16
  44. package/dist/chunks/{index-CS0VbiLD.js → index-C10-EOdB.js} +1 -1
  45. package/dist/chunks/{index-2BOEcJIa.js → index-C4JmZLaj.js} +2 -2
  46. package/dist/chunks/index-CA-JKLr4.js +2 -0
  47. package/dist/chunks/{index-43GiH--T.js → index-CG_jJZVu.js} +2 -2
  48. package/dist/chunks/{index-BdkFKsMm.js → index-CGyP2uDE.js} +3 -3
  49. package/dist/chunks/{index-BOKv0b01.js → index-CPl1s9XJ.js} +2 -2
  50. package/dist/chunks/{index-DIPrBH-4.js → index-CSxE4cEm.js} +9 -9
  51. package/dist/chunks/{index-Cm7U1-fU.js → index-CWR5EzXx.js} +2 -2
  52. package/dist/chunks/{index-BwN5llCR.js → index-Camql5kL.js} +2 -2
  53. package/dist/chunks/{index-Drh-J9O7.js → index-CdUiTipO.js} +7 -7
  54. package/dist/chunks/{index-Cn029KTN.js → index-Crg61pK5.js} +2 -2
  55. package/dist/chunks/{index-Dja2O-hV.js → index-Cv9ZYXso.js} +1 -1
  56. package/dist/chunks/{index-Ck8v_Gv7.js → index-D7ADMlG3.js} +1 -1
  57. package/dist/chunks/index-DNl2gC8l.js +158 -0
  58. package/dist/chunks/{index-BYIzl-Ut.js → index-DVJA0qVm.js} +36 -36
  59. package/dist/chunks/{index-BAEIZVx0.js → index-DcFJhkY1.js} +2 -2
  60. package/dist/chunks/{index-Dx5_CB2E.js → index-DdEd-L3x.js} +2 -2
  61. package/dist/chunks/{index-DVmqMmgb.js → index-DkGA1eW_.js} +32 -32
  62. package/dist/chunks/{index-3k7mPX1e.js → index-Dnwxlti3.js} +1 -1
  63. package/dist/chunks/{index-CrizvxOJ.js → index-DqdEJDi2.js} +2 -2
  64. package/dist/chunks/{index-BIPebtDl.js → index-DqiwmBt4.js} +1 -1
  65. package/dist/chunks/{index-DS4MhAw-.js → index-PfuqBWjY.js} +2 -2
  66. package/dist/chunks/{index-BmFT1Kx2.js → index-SVP6RfVl.js} +2 -2
  67. package/dist/chunks/{index-CDxV4b81.js → index-Y8wu48Q0.js} +1 -1
  68. package/dist/chunks/index-_ewSKppm.js +2 -0
  69. package/dist/chunks/{index-Ctcr_Qeu.js → index-aBVtCLIJ.js} +2 -2
  70. package/dist/chunks/index-dNdz9j6d.js +2 -0
  71. package/dist/chunks/{index-BFUkVkSo.js → index-gskOpu3H.js} +16 -16
  72. package/dist/chunks/{index-CXpkT25O.js → index-pBSLMn5Q.js} +3 -3
  73. package/dist/chunks/index-sqPhX5Io.js +2 -0
  74. package/dist/chunks/{index-BzmR4Es2.js → index-vfrjhUD1.js} +39 -39
  75. package/dist/chunks/{index-J2NvjjDV.js → index-xGF5XC_i.js} +2 -2
  76. package/dist/chunks/{run-review-command.logic-CA2Rjymb.js → run-review-command.logic-B9GoCxh-.js} +24 -24
  77. package/dist/chunks/{serve.cmd-RxYS6WoN.js → serve.cmd-UXkaeaPI.js} +2 -2
  78. package/dist/chunks/{services-Csd556Tv.js → services-BYCeGBlO.js} +4 -4
  79. package/dist/chunks/{shared-B5CTGvpG.js → shared-B3cXL2Np.js} +1178 -1117
  80. package/dist/chunks/{stats-index-BLztvK69.js → stats-index-AIAnnp9S.js} +2 -2
  81. package/dist/gennady.js +46 -46
  82. package/dist/index.js +4 -4
  83. package/dist/shared/sdd/ladder.d.ts +42 -0
  84. package/dist/shared/sdd/module-specs.d.ts +7 -0
  85. package/dist/shared/sdd/tracker.d.ts +9 -0
  86. package/package.json +1 -1
  87. package/dist/chunks/classify-mr-stage.logic-ClmhzQPQ.js +0 -53
  88. package/dist/chunks/config-index-RFeqlDWs.js +0 -104
  89. package/dist/chunks/devtools-p8tmPuLn.js +0 -79
  90. package/dist/chunks/eval.cmd-ewU5lcUY.js +0 -34
  91. package/dist/chunks/help-DzGnMNi2.js +0 -7
  92. package/dist/chunks/index--AXFi0ty.js +0 -89
  93. package/dist/chunks/index-64TSesTc.js +0 -186
  94. package/dist/chunks/index-9Gv5trS_.js +0 -125
  95. package/dist/chunks/index-B1DQIryA.js +0 -2
  96. package/dist/chunks/index-B1PiBeQ3.js +0 -2
  97. package/dist/chunks/index-B4rzuhkt.js +0 -183
  98. package/dist/chunks/index-B97HJjkg.js +0 -159
  99. package/dist/chunks/index-BF6qypm0.js +0 -131
  100. package/dist/chunks/index-BK84ZQsh.js +0 -128
  101. package/dist/chunks/index-BRVIirjj.js +0 -141
  102. package/dist/chunks/index-BXnryCGW.js +0 -96
  103. package/dist/chunks/index-BaZm7Otb.js +0 -2
  104. package/dist/chunks/index-Bhk746A2.js +0 -129
  105. package/dist/chunks/index-Bi-eY_dY.js +0 -164
  106. package/dist/chunks/index-BjumU69g.js +0 -2
  107. package/dist/chunks/index-BkWXcpXU.js +0 -16979
  108. package/dist/chunks/index-BlY4jQIA.js +0 -168
  109. package/dist/chunks/index-C0iy7GxD.js +0 -140
  110. package/dist/chunks/index-CCBTXczb.js +0 -56
  111. package/dist/chunks/index-CDAfkSrr.js +0 -2
  112. package/dist/chunks/index-CDHymWzC.js +0 -102
  113. package/dist/chunks/index-CNfXJAao.js +0 -2
  114. package/dist/chunks/index-CQsae1nq.js +0 -207
  115. package/dist/chunks/index-CVmd0prf.js +0 -2
  116. package/dist/chunks/index-CZuTNeNm.js +0 -2
  117. package/dist/chunks/index-CasNhhrL.js +0 -96
  118. package/dist/chunks/index-Ce7GWENF.js +0 -377
  119. package/dist/chunks/index-Cf-02TAg.js +0 -265
  120. package/dist/chunks/index-CfjQWSrh.js +0 -52
  121. package/dist/chunks/index-Chr7gBBz.js +0 -443
  122. package/dist/chunks/index-Ci875Zlg.js +0 -346
  123. package/dist/chunks/index-CwA6RzIB.js +0 -3793
  124. package/dist/chunks/index-D1dI63ej.js +0 -46
  125. package/dist/chunks/index-DCS32A2i.js +0 -663
  126. package/dist/chunks/index-DOFoNxvb.js +0 -131
  127. package/dist/chunks/index-DVkCEuws.js +0 -136
  128. package/dist/chunks/index-DkadaaXq.js +0 -658
  129. package/dist/chunks/index-Dn11CLtk.js +0 -7
  130. package/dist/chunks/index-Do3spFJT.js +0 -340
  131. package/dist/chunks/index-DrFRZ0Yg.js +0 -2
  132. package/dist/chunks/index-DvEQhXi2.js +0 -1054
  133. package/dist/chunks/index-GiS0x6jg.js +0 -90
  134. package/dist/chunks/index-NacvqeQ5.js +0 -69
  135. package/dist/chunks/index-RNJhFzdr.js +0 -227
  136. package/dist/chunks/index-R_vUB6CL.js +0 -2
  137. package/dist/chunks/index-TQft1s5H.js +0 -20
  138. package/dist/chunks/index-ZmQRP38W.js +0 -26969
  139. package/dist/chunks/index-jdOZ4AkD.js +0 -143
  140. package/dist/chunks/index-kNa21kUI.js +0 -279
  141. package/dist/chunks/index-v4AV9kAc.js +0 -108
  142. package/dist/chunks/run-review-command.logic-CU1Slmc_.js +0 -331
  143. package/dist/chunks/serve.cmd-dWu_k_G_.js +0 -162
  144. package/dist/chunks/services-CwvXl2MB.js +0 -46832
  145. package/dist/chunks/shared-DSZTUx5b.js +0 -4509
  146. package/dist/chunks/stats-index-H6ALcjxy.js +0 -66
@@ -1,46 +0,0 @@
1
- import { fileURLToPath as g } from "node:url";
2
- import { l as i, n as h, ab as b } from "./services-CwvXl2MB.js";
3
- function l(t, r) {
4
- const s = t.find((o) => o.startsWith(`${r}=`));
5
- if (s) return s.slice(r.length + 1);
6
- const e = t.indexOf(r);
7
- return e !== -1 ? t[e + 1] : void 0;
8
- }
9
- function S(t) {
10
- return t.split(",").map((r) => r.trim()).filter(Boolean);
11
- }
12
- async function v(t = process.argv, r = {}) {
13
- const s = t.slice(2), e = r.stdout ?? process.stdout, o = r.stderr ?? process.stderr, a = l(s, "--mrs");
14
- if (!a)
15
- return o.write(`Error: --mrs <urls> is required
16
- `), 1;
17
- const d = l(s, "--seed"), m = l(s, "--waf"), p = m !== void 0 ? Number(m) : void 0, f = !s.includes("--no-dry-run"), x = s.includes("--mocks");
18
- try {
19
- i.info("[inboxEvalCmd#run] [idle → running]", { mrs: a, dryRun: f });
20
- const n = d ? await h(d) : void 0, c = {
21
- mrs: S(a),
22
- seedState: n,
23
- wafThreshold: p,
24
- dryRun: f
25
- }, E = r.runEval ?? b, { report: u, reportDir: w } = await E(c, { ...r, mocks: r.mocks ?? x });
26
- return e.write(`Eval report: ${w}/eval-report.json
27
- `), e.write(`Status: ${u.status}
28
- `), i.info("[inboxEvalCmd#run] [running → done]", { status: u.status }), u.status === "PASS" ? 0 : 1;
29
- } catch (n) {
30
- const c = new Error("[inboxEvalCmd#run] Eval run failed", { cause: n });
31
- return i.error("[inboxEvalCmd#run] [running → failed]", { error: c }), o.write(`Error: ${n.message ?? String(n)}
32
- `), 1;
33
- }
34
- }
35
- if (process.argv[1]) {
36
- const t = g(import.meta.url);
37
- if (t === process.argv[1] || t.endsWith(process.argv[1]))
38
- try {
39
- const r = await v(process.argv);
40
- process.exit(r);
41
- } catch (r) {
42
- const s = new Error("[inboxEvalCmd] Self-execution failed", { cause: r });
43
- i.error("[inboxEvalCmd#run] [self-executing → failed]", { error: s }), process.exit(1);
44
- }
45
- }
46
- process.exit(await v());
@@ -1,663 +0,0 @@
1
- import { execSync as je } from "node:child_process";
2
- import { existsSync as S, readFileSync as I, lstatSync as ne, readdirSync as A } from "node:fs";
3
- import { resolve as me, join as m, basename as V, relative as Z, extname as M } from "node:path";
4
- import { p as xe } from "./shared-B5CTGvpG.js";
5
- function He(e) {
6
- let t = 0, n = 0;
7
- for (const o of e)
8
- t += o.sH, n += o.sT;
9
- return { hit: t, total: n };
10
- }
11
- function he(e) {
12
- return e.total > 0 ? 100 * e.hit / e.total : null;
13
- }
14
- function Oe(e, t) {
15
- const n = he(e);
16
- return n !== null && n >= t;
17
- }
18
- const p = me(process.cwd()), k = m(p, "coverage", "coverage-final.json"), se = m(p, "coverage", ".tree-results.json"), g = xe(process.argv, {
19
- files: ["files"],
20
- run: ["run"],
21
- check: ["check"],
22
- json: ["json"],
23
- flat: ["flat"],
24
- help: ["help", "h"],
25
- context: ["context", "c"],
26
- color: ["color"],
27
- min: ["min"]
28
- }), we = g.run === !0 || g.run === "true", ye = g.check === !0 || g.check === "true", q = g.files === !0 || g.files === "true", _e = g.flat === !0 || g.flat === "true", ve = g.json === !0 || g.json === "true", Se = g.help === !0 || g.help === "true", Re = typeof g.context == "string" && parseInt(g.context, 10) || 2, ee = g.color === !0 || g.color === "true", E = typeof g.min == "string" ? parseFloat(g.min) : void 0;
29
- g.min === !0 && (console.error("testcov: --min requires a value, e.g. --min=80"), process.exit(4));
30
- E !== void 0 && (Number.isNaN(E) || E < 0) && (console.error(`testcov: --min value must be a non-negative number, got "${String(g.min)}"`), process.exit(4));
31
- const pe = g._.filter((e) => e !== "testcov"), F = pe.length > 0 ? pe[0] : void 0;
32
- if (Se) {
33
- const { printHelp: e } = await import("./help-C1rYrToJ.js");
34
- e(), process.exit(0);
35
- }
36
- function de() {
37
- const e = m(p, "package.json");
38
- if (!S(e)) return null;
39
- try {
40
- return JSON.parse(I(e, "utf8"));
41
- } catch {
42
- return null;
43
- }
44
- }
45
- function ae() {
46
- const e = de();
47
- if (!e) return [];
48
- const t = { ...e.devDependencies ?? {}, ...e.dependencies ?? {} }, n = e.scripts ?? {}, o = [];
49
- (t.vitest || t["@vitest/coverage-v8"] || t["@vitest/coverage-istanbul"]) && o.push({
50
- name: "vitest",
51
- runCmd: (s) => `npx vitest run --coverage --reporter=default --reporter=json --outputFile.json=${s}`
52
- }), (t.jest || t["@jest/core"] || t["jest-circus"] || t["babel-jest"]) && o.push({
53
- name: "jest",
54
- runCmd: (s) => `npx jest --coverage --json --outputFile=${s}`
55
- });
56
- const r = Object.entries(n).find(([, s]) => /\bnode\s+--test\b/.test(s));
57
- if (t.c8 && r) {
58
- const [s, c] = r, f = /\bc8\b/.test(c);
59
- o.push({
60
- name: "node:test",
61
- runCmd: (a) => f ? `npm run ${s}` : `npx c8 --reporter=json npm run ${s}`
62
- });
63
- }
64
- return o;
65
- }
66
- function Ne(e) {
67
- const t = e.scripts ?? {};
68
- return Object.entries(t).find(
69
- ([, o]) => /\bnode\s+--test\b/.test(o) && /--experimental-test-coverage\b/.test(o)
70
- ) ?? null;
71
- }
72
- function le() {
73
- const e = [], t = de();
74
- if (!t)
75
- return e.push({
76
- level: "error",
77
- code: "NO_PACKAGE_JSON",
78
- message: "package.json not found at project root",
79
- expect: `package.json at ${p}`,
80
- fix: "Run from the project root (directory containing package.json)"
81
- }), e;
82
- const n = ae();
83
- if (n.length === 0) {
84
- const r = Ne(t);
85
- if (r) {
86
- const [s] = r;
87
- return e.push({
88
- level: "error",
89
- code: "NATIVE_COVERAGE_UNSUPPORTED",
90
- message: `native node coverage found: "${s}" runs "node --test --experimental-test-coverage" — but testcov's tree/gate pipeline reads c8's Istanbul JSON output, which node's own --experimental-test-coverage does not produce`,
91
- expect: 'c8 with a "node --test" npm script (Istanbul-format coverage-final.json)',
92
- fix: `native node coverage found; install c8 for testcov integration — npm install -D c8, then wrap the script: npx c8 --reporter=json npm run ${s}`
93
- }), e;
94
- }
95
- return e.push({
96
- level: "error",
97
- code: "NO_RUNNER",
98
- message: "No supported test runner detected in package.json",
99
- expect: 'devDependencies must include one of: vitest, @vitest/coverage-v8, jest, @jest/core, jest-circus — or c8 with a "node --test" npm script',
100
- fix: "Install a runner: npm install -D vitest @vitest/coverage-v8"
101
- }), e;
102
- }
103
- const o = n[0];
104
- if (!S(k)) {
105
- const r = o.name === "vitest" ? "npx vitest run --coverage" : o.name === "jest" ? "npx jest --coverage" : "npm test";
106
- e.push({
107
- level: "error",
108
- code: "NO_COVERAGE_FILE",
109
- message: "coverage/coverage-final.json not found — tests have not been run with coverage",
110
- expect: `Istanbul JSON coverage file at: ${k}`,
111
- fix: `Option A: npx gennady testcov --run
112
- Option B: ${r} (then re-run without --run)`
113
- });
114
- }
115
- return o.name === "vitest" ? Fe(e) : o.name === "jest" && Ce(e, t), e;
116
- }
117
- function Fe(e) {
118
- const n = ["vitest.config.ts", "vitest.config.js", "vitest.config.mts"].find((s) => S(m(p, s)));
119
- if (!n) {
120
- e.push({
121
- level: "warning",
122
- code: "NO_RUNNER_CONFIG",
123
- message: "No vitest config file found",
124
- expect: "vitest.config.ts at project root with coverage.reporter and coverage.reportOnFailure",
125
- fix: "Create vitest.config.ts — see: https://vitest.dev/config/#coverage"
126
- });
127
- return;
128
- }
129
- const o = I(m(p, n), "utf8");
130
- /reporter\s*:\s*\[[^\]]*['"]json['"]/s.test(o) || e.push({
131
- level: "error",
132
- code: "MISSING_JSON_REPORTER",
133
- message: `${n}: coverage.reporter is missing 'json'`,
134
- expect: "coverage: { reporter: ['json', 'text', ...] }",
135
- fix: `Add 'json' to coverage.reporter in ${n} — without it, coverage-final.json is never written`
136
- }), /\breportOnFailure\s*:\s*false\b/.test(o) ? e.push({
137
- level: "error",
138
- code: "REPORT_ON_FAILURE_DISABLED",
139
- message: `${n}: reportOnFailure is explicitly set to false`,
140
- expect: "coverage: { reportOnFailure: true }",
141
- fix: `Change reportOnFailure: false → true in ${n} — when false, coverage is skipped whenever any test fails`
142
- }) : /\breportOnFailure\s*:\s*true\b/.test(o) || e.push({
143
- level: "warning",
144
- code: "MISSING_REPORT_ON_FAILURE",
145
- message: `${n}: coverage.reportOnFailure is not set`,
146
- expect: "coverage: { reportOnFailure: true }",
147
- fix: "Add reportOnFailure: true to the coverage block — without it, coverage is skipped when any test fails (e.g. browser-only tests)"
148
- });
149
- }
150
- function Ce(e, t) {
151
- const o = [
152
- "jest.config.ts",
153
- "jest.config.js",
154
- "jest.config.mjs",
155
- "jest.config.cjs"
156
- ].find((s) => S(m(p, s)));
157
- let r = !1;
158
- if (o) {
159
- const s = I(m(p, o), "utf8");
160
- r = /coverageReporters\s*:\s*\[[^\]]*['"]json['"]/s.test(s);
161
- } else t.jest?.coverageReporters && (r = t.jest.coverageReporters.includes("json"));
162
- if (!r) {
163
- const s = o ?? "package.json (jest.coverageReporters)";
164
- e.push({
165
- level: "error",
166
- code: "MISSING_JSON_REPORTER",
167
- message: "jest config missing coverageReporters: ['json', ...]",
168
- expect: `coverageReporters: ['json', 'text', ...] in ${s}`,
169
- fix: "Add 'json' to coverageReporters — without it, coverage-final.json is never written"
170
- });
171
- }
172
- }
173
- function z(e, t) {
174
- if (t) {
175
- const s = ae();
176
- process.stdout.write(
177
- JSON.stringify(
178
- {
179
- ok: e.every((c) => c.level !== "error"),
180
- runner: s[0]?.name ?? null,
181
- coverageFile: k,
182
- diagnostics: e
183
- },
184
- null,
185
- 2
186
- ) + `
187
- `
188
- );
189
- return;
190
- }
191
- if (e.length === 0) {
192
- process.stderr.write(`testcov: ✅ configuration OK
193
- `);
194
- return;
195
- }
196
- const n = e.filter((s) => s.level === "error"), o = e.filter((s) => s.level === "warning"), r = [
197
- n.length > 0 ? `${n.length} error(s)` : "",
198
- o.length > 0 ? `${o.length} warning(s)` : ""
199
- ].filter(Boolean).join(", ");
200
- process.stderr.write(`testcov: ${r}
201
-
202
- `);
203
- for (const s of e) {
204
- const c = s.level === "error" ? "✗" : "⚠";
205
- process.stderr.write(` ${c} [${s.code}] ${s.message}
206
- `), process.stderr.write(` Expect: ${s.expect}
207
- `), process.stderr.write(` Fix: ${s.fix}
208
-
209
- `);
210
- }
211
- }
212
- if (ye) {
213
- const e = le();
214
- z(e, ve), process.exit(e.some((t) => t.level === "error") ? 1 : 0);
215
- }
216
- if (we) {
217
- const t = ae()[0];
218
- t || (z(le(), !1), process.exit(1)), process.stderr.write(`testcov: running ${t.name} with coverage...
219
- `);
220
- try {
221
- je(t.runCmd(se), { stdio: ["ignore", "inherit", "inherit"], cwd: p });
222
- } catch (n) {
223
- const o = n.status ?? -1;
224
- o !== 1 && (process.stderr.write(
225
- `
226
- testcov: ⚠ ${t.name} exited with code ${o} — possible configuration error.
227
- `
228
- ), process.stderr.write(` Run: npx gennady testcov --check to diagnose.
229
- `));
230
- }
231
- }
232
- S(k) || (z(le(), !1), process.exit(1));
233
- let fe;
234
- try {
235
- fe = JSON.parse(I(k, "utf8"));
236
- } catch {
237
- z(
238
- [
239
- {
240
- level: "error",
241
- code: "COVERAGE_FILE_PARSE_ERROR",
242
- message: "coverage-final.json is not valid JSON",
243
- expect: `Valid Istanbul-format JSON at: ${k}`,
244
- fix: "Delete the file and re-run: npx gennady testcov --run"
245
- }
246
- ],
247
- !1
248
- ), process.exit(1);
249
- }
250
- const ue = {};
251
- for (const [e, t] of Object.entries(fe)) {
252
- const n = Object.values(t.s ?? {}), o = Object.values(t.b ?? {}).flat(), r = Object.values(t.f ?? {});
253
- ue[e] = {
254
- sT: n.length,
255
- sH: n.filter((s) => s > 0).length,
256
- bT: o.length,
257
- bH: o.filter((s) => s > 0).length,
258
- fT: r.length,
259
- fH: r.filter((s) => s > 0).length
260
- };
261
- }
262
- const be = {};
263
- if (S(se))
264
- try {
265
- const e = JSON.parse(I(se, "utf8"));
266
- for (const t of e.testResults ?? []) {
267
- const n = t.testFilePath ?? t.name;
268
- n && (be[n] = (t.assertionResults ?? []).filter(
269
- (o) => o.status === "passed"
270
- ).length);
271
- }
272
- } catch {
273
- }
274
- const P = /* @__PURE__ */ new Set([
275
- "node_modules",
276
- ".git",
277
- "dist",
278
- "build",
279
- "out",
280
- "coverage",
281
- ".vite",
282
- ".cache",
283
- ".turbo",
284
- ".nx",
285
- "__generated__",
286
- ".next",
287
- ".nuxt",
288
- ".svelte-kit",
289
- "vendor",
290
- "third_party",
291
- "external",
292
- ".storybook",
293
- ".husky",
294
- ".claude",
295
- ".github",
296
- "__tests__",
297
- "__snapshots__",
298
- "__mocks__",
299
- "docs",
300
- "public",
301
- "static",
302
- "assets",
303
- "fixtures",
304
- "__fixtures__",
305
- "tooling-lab",
306
- "draft",
307
- "tasks",
308
- "specs",
309
- "ai"
310
- ]), J = /* @__PURE__ */ new Set([
311
- ".ts",
312
- ".tsx",
313
- ".mts",
314
- ".cts",
315
- ".svelte",
316
- ".vue",
317
- ".js",
318
- ".jsx",
319
- ".mjs",
320
- ".cjs"
321
- ]), W = (e) => /\.(test|spec)\.(ts|tsx|mts|js|jsx|mjs|svelte|vue)$/.test(e);
322
- function Ee(e, t) {
323
- if (t[e]) return t[e];
324
- const n = V(e);
325
- for (const [o, r] of Object.entries(t))
326
- if (V(o) === n) return r;
327
- }
328
- const oe = {};
329
- for (const [e, t] of Object.entries(ue)) {
330
- const n = V(e);
331
- oe[n] || (oe[n] = t);
332
- }
333
- function Y(e) {
334
- return ue[e] ?? oe[V(e)];
335
- }
336
- function L(e) {
337
- try {
338
- return ne(e).isSymbolicLink();
339
- } catch {
340
- return !1;
341
- }
342
- }
343
- function h(e, t) {
344
- return t > 0 ? Math.round(100 * e / t) : null;
345
- }
346
- function ge(e) {
347
- const t = me(p, e);
348
- if (S(t)) return [t];
349
- const n = V(e), o = [], r = [p];
350
- for (; r.length > 0; ) {
351
- const s = r.pop();
352
- let c;
353
- try {
354
- c = A(s, { withFileTypes: !0 });
355
- } catch {
356
- continue;
357
- }
358
- for (const f of c) {
359
- const a = m(s, f.name);
360
- L(a) || (f.isDirectory() ? !P.has(f.name) && !f.name.startsWith(".") && r.push(a) : f.name === n && J.has(M(f.name)) && !W(f.name) && o.push(a));
361
- }
362
- }
363
- return o;
364
- }
365
- function U(e) {
366
- return e === null ? "⚫" : e >= 75 ? "✅" : e >= 50 ? "🟢" : e >= 25 ? "🟡" : e > 0 ? "🟠" : "🔴";
367
- }
368
- function ke(e, t) {
369
- if (!S(e)) return null;
370
- const o = I(e, "utf8").split(`
371
- `), r = /* @__PURE__ */ new Map();
372
- for (let u = 0; u < o.length; u++)
373
- r.set(u + 1, {
374
- num: u + 1,
375
- text: o[u],
376
- sT: 0,
377
- sH: 0,
378
- bT: 0,
379
- bH: 0,
380
- fT: 0,
381
- fH: 0
382
- });
383
- const s = t.statementMap ?? {}, c = t.s ?? {};
384
- for (const [u, $] of Object.entries(s)) {
385
- const H = c[u] ?? 0;
386
- for (let d = $.start.line; d <= $.end.line; d++) {
387
- const T = r.get(d);
388
- T && (T.sT++, H > 0 && T.sH++);
389
- }
390
- }
391
- const f = t.branchMap ?? {}, a = t.b ?? {};
392
- for (const [u, $] of Object.entries(f)) {
393
- const H = a[u] ?? [];
394
- for (let d = 0; d < $.locations.length; d++) {
395
- const T = $.locations[d];
396
- for (let R = T.start.line; R <= T.end.line; R++) {
397
- const G = r.get(R);
398
- G && (G.bT++, (H[d] ?? 0) > 0 && G.bH++);
399
- }
400
- }
401
- }
402
- const B = t.fnMap ?? {}, b = t.f ?? {};
403
- for (const [u, $] of Object.entries(B)) {
404
- const H = b[u] ?? 0;
405
- for (let d = $.loc.start.line; d <= $.loc.end.line; d++) {
406
- const T = r.get(d);
407
- T && (T.fT++, H > 0 && T.fH++);
408
- }
409
- }
410
- let j = 0, v = 0, x = 0, y = 0, _ = 0, C = 0;
411
- for (const u of r.values())
412
- j += u.sT, v += u.sH, x += u.bT, y += u.bH, _ += u.fT, C += u.fH;
413
- return {
414
- path: e,
415
- lines: [...r.values()],
416
- sT: j,
417
- sH: v,
418
- bT: x,
419
- bH: y,
420
- fT: _,
421
- fH: C
422
- };
423
- }
424
- function De(e, t, n) {
425
- const o = h(e.sH, e.sT), r = h(e.bH, e.bT), s = h(e.fH, e.fT), c = o !== null ? `${o}%` : "—", f = r !== null ? `${r}%` : "—", a = s !== null ? `${s}%` : "—", B = e.path.startsWith(p) ? e.path.slice(p.length + 1) : e.path, b = `s:${e.sH}/${e.sT} b:${e.bH}/${e.bT} f:${e.fH}/${e.fT}`;
426
- console.log(`
427
- ── 📄 ${B} — ${U(o)} ${c}/${f}/${a} (${b}) ──
428
- `);
429
- const j = [];
430
- let v = -1;
431
- for (const i of e.lines) {
432
- const l = i.sT > 0 && i.sH === 0;
433
- l && v === -1 ? v = i.num : !l && v !== -1 && (j.push({ start: v, end: i.num - 1 }), v = -1);
434
- }
435
- v !== -1 && j.push({ start: v, end: e.lines.length });
436
- const x = /* @__PURE__ */ new Set();
437
- for (const i of e.lines)
438
- i.sT > 0 && i.sH > 0 && i.sH < i.sT && x.add(i.num);
439
- const y = /* @__PURE__ */ new Set();
440
- for (const i of j)
441
- for (let l = i.start; l <= i.end; l++) y.add(l);
442
- for (const i of x) y.add(i);
443
- const _ = /* @__PURE__ */ new Set();
444
- for (const i of y)
445
- for (let l = Math.max(1, i - t); l <= Math.min(e.lines.length, i + t); l++)
446
- _.add(l);
447
- const C = _.size === 0;
448
- let u = 0, $ = !1;
449
- const H = /* @__PURE__ */ new Map(), d = n.branchMap ?? {}, T = n.b ?? {};
450
- for (const [i, l] of Object.entries(d)) {
451
- const K = T[i] ?? [], O = l.locations.length, N = K.filter((w) => w > 0).length;
452
- if (N < O) {
453
- const w = l.line ?? l.locations[0]?.start.line ?? 0;
454
- if (w > 0 && !H.has(w)) {
455
- const Q = N === 0 ? "not taken" : `${N}/${O} taken`;
456
- H.set(w, `← branch ${Q}`);
457
- }
458
- }
459
- }
460
- const R = /* @__PURE__ */ new Map(), G = n.fnMap ?? {}, Te = n.f ?? {};
461
- for (const [i, l] of Object.entries(G))
462
- if ((Te[i] ?? 0) === 0) {
463
- const O = l.line ?? l.loc.start.line;
464
- if (O > 0 && !R.has(O)) {
465
- const N = !l.name || l.name === "(anonymous)" || l.name === "__name" || l.name.startsWith("(anonymous");
466
- R.set(O, N ? "← never called" : `← ${l.name}() never called`);
467
- }
468
- }
469
- for (const i of e.lines) {
470
- if (!C && !_.has(i.num)) {
471
- !$ && u > 0 && u < i.num - 1 && (console.log(" ···"), $ = !0);
472
- continue;
473
- }
474
- $ = !1, u = i.num;
475
- const l = String(i.num).padStart(5, " "), K = Ie(i), O = ee && i.sT > 0 && i.sH === 0 ? "\x1B[41m\x1B[37m" : ee && i.sT > 0 && i.sH < i.sT ? "\x1B[43m\x1B[30m" : "", N = O ? "\x1B[0m" : "", w = H.get(i.num) ?? R.get(i.num), Q = w ? ee ? ` \x1B[33m${w}\x1B[0m` : ` ${w}` : "";
476
- console.log(`${O}${l} ${K} ${i.text}${N}${Q}`);
477
- }
478
- console.log(`
479
- ${U(null)} not instrumented ♦️ uncovered 🔸 partial ✓ covered`);
480
- }
481
- function Ie(e) {
482
- return e.sT === 0 && e.bT === 0 && e.fT === 0 ? "·" : e.sT > 0 && e.sH === 0 ? "♦️" : e.sT > 0 && e.sH < e.sT || e.bT > 0 && e.bH < e.bT ? "🔸" : e.fT > 0 && e.fH === 0 ? "♦️" : e.fT > 0 && e.fH < e.fT ? "🔸" : "✓";
483
- }
484
- const te = /* @__PURE__ */ new Map();
485
- function D(e) {
486
- const t = te.get(e);
487
- if (t) return t;
488
- const n = { sT: 0, sH: 0, bT: 0, bH: 0, fT: 0, fH: 0, cases: 0 };
489
- let o;
490
- try {
491
- o = A(e, { withFileTypes: !0 });
492
- } catch {
493
- return te.set(e, n), n;
494
- }
495
- for (const r of o) {
496
- const s = m(e, r.name);
497
- if (!L(s))
498
- if (r.isDirectory()) {
499
- if (P.has(r.name)) continue;
500
- const c = D(s);
501
- n.sT += c.sT, n.sH += c.sH, n.bT += c.bT, n.bH += c.bH, n.fT += c.fT, n.fH += c.fH, n.cases += c.cases;
502
- } else {
503
- if (!J.has(M(r.name))) continue;
504
- if (W(r.name))
505
- n.cases += be[s] ?? 0;
506
- else {
507
- const c = Y(s);
508
- c && (n.sT += c.sT, n.sH += c.sH, n.bT += c.bT, n.bH += c.bH, n.fT += c.fT, n.fH += c.fH);
509
- }
510
- }
511
- }
512
- return te.set(e, n), n;
513
- }
514
- function re(e) {
515
- const t = h(e.sH, e.sT), n = t !== null ? `${U(t)} ${t}%` : U(null);
516
- return e.cases > 0 ? `${n} (${e.cases} tests)` : n;
517
- }
518
- function ce(e, t) {
519
- let n;
520
- try {
521
- n = A(e, { withFileTypes: !0 });
522
- } catch {
523
- return;
524
- }
525
- const o = n.filter((r) => {
526
- const s = m(e, r.name);
527
- return L(s) ? !1 : r.isDirectory() ? !P.has(r.name) : !q || W(r.name) ? !1 : J.has(M(r.name));
528
- });
529
- o.sort((r, s) => r.isDirectory() !== s.isDirectory() ? r.isDirectory() ? -1 : 1 : r.name.localeCompare(s.name));
530
- for (let r = 0; r < o.length; r++) {
531
- const s = o[r], c = r === o.length - 1, f = m(e, s.name), a = c ? "└─" : "├─", B = t + (c ? " " : "│ ");
532
- if (s.isDirectory()) {
533
- const b = D(f);
534
- console.log(`${t}${a} 📁 ${s.name} — ${re(b)}`), ce(f, B);
535
- } else {
536
- const b = Y(f);
537
- if (b) {
538
- const j = h(b.sH, b.sT), v = h(b.bH, b.bT), x = h(b.fH, b.fT);
539
- if (j === null && v === null && x === null)
540
- console.log(`${t}${a} 📄 ${s.name} ⚫`);
541
- else {
542
- const y = j !== null ? `${j}%` : "—", _ = v !== null ? `${v}%` : "—", C = x !== null ? `${x}%` : "—";
543
- console.log(`${t}${a} 📄 ${s.name} — ${U(j)} ${y}/${_}/${C}`);
544
- }
545
- } else
546
- console.log(`${t}${a} 📄 ${s.name} ⚫`);
547
- }
548
- }
549
- }
550
- function $e(e, t = 0) {
551
- if (t > 4) return !1;
552
- let n;
553
- try {
554
- n = A(e, { withFileTypes: !0 });
555
- } catch {
556
- return !1;
557
- }
558
- for (const o of n)
559
- if (o.isFile() && J.has(M(o.name)) || o.isDirectory() && !P.has(o.name) && !L(m(e, o.name)) && $e(m(e, o.name), t + 1))
560
- return !0;
561
- return !1;
562
- }
563
- function ie() {
564
- return A(p, { withFileTypes: !0 }).filter((e) => !e.isDirectory() || P.has(e.name) || e.name.startsWith(".") || L(m(p, e.name)) ? !1 : $e(m(p, e.name))).map((e) => e.name).sort();
565
- }
566
- function X(e, t, n) {
567
- let o;
568
- try {
569
- o = A(e, { withFileTypes: !0 });
570
- } catch {
571
- return;
572
- }
573
- const r = [...o].sort((s, c) => s.isDirectory() !== c.isDirectory() ? s.isDirectory() ? -1 : 1 : s.name.localeCompare(c.name));
574
- for (const s of r) {
575
- const c = m(e, s.name), f = m(t, s.name);
576
- if (!L(c))
577
- if (s.isDirectory()) {
578
- if (P.has(s.name)) continue;
579
- if (q)
580
- X(c, f, n);
581
- else {
582
- const a = D(c);
583
- n.push({
584
- path: f,
585
- lines: h(a.sH, a.sT),
586
- branches: h(a.bH, a.bT),
587
- functions: h(a.fH, a.fT),
588
- tests: a.cases || void 0
589
- }), X(c, f, n);
590
- }
591
- } else {
592
- if (!q || !J.has(M(s.name)) || W(s.name)) continue;
593
- const a = Y(c);
594
- a ? n.push({
595
- path: f,
596
- lines: h(a.sH, a.sT),
597
- branches: h(a.bH, a.bT),
598
- functions: h(a.fH, a.fT)
599
- }) : n.push({ path: f, lines: null, branches: null, functions: null });
600
- }
601
- }
602
- }
603
- function Ae(e) {
604
- if (ve) {
605
- console.log(JSON.stringify(e, null, 2));
606
- return;
607
- }
608
- for (const t of e) {
609
- const n = t.lines !== null ? `${t.lines}%` : "—", o = t.branches !== null ? `${t.branches}%` : "—", r = t.functions !== null ? `${t.functions}%` : "—", s = q ? `${n}/${o}/${r}` : n, c = t.tests !== void 0 ? ` (${t.tests} tests)` : "";
610
- console.log(`${t.path} ${s}${c}`);
611
- }
612
- }
613
- if (E !== void 0) {
614
- const e = He(ie().map((r) => D(m(p, r)))), t = he(e), n = t !== null ? `${t.toFixed(1)}%` : "n/a (no instrumented statements found)", o = Oe(e, E);
615
- console.log(
616
- `testcov: line coverage ${n} (${e.hit}/${e.total} statements) — required ≥${E}% ${o ? "✅" : "❌"}`
617
- ), process.exit(o ? 0 : 1);
618
- }
619
- if (_e) {
620
- const e = [];
621
- if (F) {
622
- const t = ge(F);
623
- t.length === 0 && (console.error(`File not found: ${F}`), process.exit(1));
624
- for (const n of t)
625
- if (ne(n).isDirectory())
626
- X(n, F, e);
627
- else {
628
- const r = Y(n), s = Z(p, n);
629
- e.push({
630
- path: s,
631
- lines: r ? h(r.sH, r.sT) : null,
632
- branches: r ? h(r.bH, r.bT) : null,
633
- functions: r ? h(r.fH, r.fT) : null
634
- });
635
- }
636
- } else
637
- for (const t of ie()) X(m(p, t), t, e);
638
- Ae(e);
639
- } else if (F) {
640
- const e = ge(F);
641
- e.length === 0 && (console.error(`File not found: ${F}`), process.exit(1));
642
- for (const t of e)
643
- if (ne(t).isFile() && J.has(M(t)) && !W(t)) {
644
- const o = Ee(t, fe);
645
- if (o && typeof o.statementMap == "object") {
646
- const s = ke(t, o);
647
- if (s) {
648
- De(s, Re, o);
649
- continue;
650
- }
651
- }
652
- const r = Z(p, t);
653
- console.log(`📄 ${r} ⚫ (not in coverage data)`);
654
- } else {
655
- const o = D(t), r = Z(p, t);
656
- console.log(`📁 ${r} — ${re(o)}`), ce(t, "");
657
- }
658
- } else
659
- for (const e of ie()) {
660
- const t = m(p, e), n = D(t);
661
- console.log(`
662
- 📁 ${e} — ${re(n)}`), ce(t, "");
663
- }