@vyuhlabs/dxkit 1.5.1 → 1.6.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.
Files changed (257) hide show
  1. package/CHANGELOG.md +264 -0
  2. package/README.md +265 -352
  3. package/THIRD_PARTY_NOTICES.md +40 -0
  4. package/dist/analyzers/developer/detailed.d.ts +26 -0
  5. package/dist/analyzers/developer/detailed.d.ts.map +1 -0
  6. package/dist/analyzers/developer/detailed.js +193 -0
  7. package/dist/analyzers/developer/detailed.js.map +1 -0
  8. package/dist/analyzers/developer/gather.d.ts +11 -0
  9. package/dist/analyzers/developer/gather.d.ts.map +1 -0
  10. package/dist/analyzers/developer/gather.js +167 -0
  11. package/dist/analyzers/developer/gather.js.map +1 -0
  12. package/dist/analyzers/developer/index.d.ts +8 -0
  13. package/dist/analyzers/developer/index.d.ts.map +1 -0
  14. package/dist/analyzers/developer/index.js +168 -0
  15. package/dist/analyzers/developer/index.js.map +1 -0
  16. package/dist/analyzers/developer/types.d.ts +49 -0
  17. package/dist/analyzers/developer/types.d.ts.map +1 -0
  18. package/dist/analyzers/developer/types.js +6 -0
  19. package/dist/analyzers/developer/types.js.map +1 -0
  20. package/dist/analyzers/docs/shallow.d.ts +9 -0
  21. package/dist/analyzers/docs/shallow.d.ts.map +1 -0
  22. package/dist/analyzers/docs/shallow.js +8 -0
  23. package/dist/analyzers/docs/shallow.js.map +1 -0
  24. package/dist/analyzers/dx/shallow.d.ts +9 -0
  25. package/dist/analyzers/dx/shallow.d.ts.map +1 -0
  26. package/dist/analyzers/dx/shallow.js +8 -0
  27. package/dist/analyzers/dx/shallow.js.map +1 -0
  28. package/dist/analyzers/evidence.d.ts +36 -0
  29. package/dist/analyzers/evidence.d.ts.map +1 -0
  30. package/dist/analyzers/evidence.js +3 -0
  31. package/dist/analyzers/evidence.js.map +1 -0
  32. package/dist/analyzers/health/actions.d.ts +10 -0
  33. package/dist/analyzers/health/actions.d.ts.map +1 -0
  34. package/dist/analyzers/health/actions.js +284 -0
  35. package/dist/analyzers/health/actions.js.map +1 -0
  36. package/dist/analyzers/health/detailed.d.ts +26 -0
  37. package/dist/analyzers/health/detailed.d.ts.map +1 -0
  38. package/dist/analyzers/health/detailed.js +147 -0
  39. package/dist/analyzers/health/detailed.js.map +1 -0
  40. package/dist/analyzers/health.d.ts +22 -0
  41. package/dist/analyzers/health.d.ts.map +1 -0
  42. package/dist/analyzers/health.js +270 -0
  43. package/dist/analyzers/health.js.map +1 -0
  44. package/dist/analyzers/index.d.ts +3 -0
  45. package/dist/analyzers/index.d.ts.map +1 -0
  46. package/dist/analyzers/index.js +6 -0
  47. package/dist/analyzers/index.js.map +1 -0
  48. package/dist/analyzers/maintainability/shallow.d.ts +9 -0
  49. package/dist/analyzers/maintainability/shallow.d.ts.map +1 -0
  50. package/dist/analyzers/maintainability/shallow.js +8 -0
  51. package/dist/analyzers/maintainability/shallow.js.map +1 -0
  52. package/dist/analyzers/quality/actions.d.ts +5 -0
  53. package/dist/analyzers/quality/actions.d.ts.map +1 -0
  54. package/dist/analyzers/quality/actions.js +158 -0
  55. package/dist/analyzers/quality/actions.js.map +1 -0
  56. package/dist/analyzers/quality/detailed.d.ts +17 -0
  57. package/dist/analyzers/quality/detailed.d.ts.map +1 -0
  58. package/dist/analyzers/quality/detailed.js +122 -0
  59. package/dist/analyzers/quality/detailed.js.map +1 -0
  60. package/dist/analyzers/quality/gather.d.ts +38 -0
  61. package/dist/analyzers/quality/gather.d.ts.map +1 -0
  62. package/dist/analyzers/quality/gather.js +279 -0
  63. package/dist/analyzers/quality/gather.js.map +1 -0
  64. package/dist/analyzers/quality/index.d.ts +12 -0
  65. package/dist/analyzers/quality/index.d.ts.map +1 -0
  66. package/dist/analyzers/quality/index.js +281 -0
  67. package/dist/analyzers/quality/index.js.map +1 -0
  68. package/dist/analyzers/quality/shallow.d.ts +9 -0
  69. package/dist/analyzers/quality/shallow.d.ts.map +1 -0
  70. package/dist/analyzers/quality/shallow.js +8 -0
  71. package/dist/analyzers/quality/shallow.js.map +1 -0
  72. package/dist/analyzers/quality/types.d.ts +66 -0
  73. package/dist/analyzers/quality/types.d.ts.map +1 -0
  74. package/dist/analyzers/quality/types.js +3 -0
  75. package/dist/analyzers/quality/types.js.map +1 -0
  76. package/dist/analyzers/remediation.d.ts +42 -0
  77. package/dist/analyzers/remediation.d.ts.map +1 -0
  78. package/dist/analyzers/remediation.js +28 -0
  79. package/dist/analyzers/remediation.js.map +1 -0
  80. package/dist/analyzers/scoring.d.ts +32 -0
  81. package/dist/analyzers/scoring.d.ts.map +1 -0
  82. package/dist/analyzers/scoring.js +410 -0
  83. package/dist/analyzers/scoring.js.map +1 -0
  84. package/dist/analyzers/security/actions.d.ts +7 -0
  85. package/dist/analyzers/security/actions.d.ts.map +1 -0
  86. package/dist/analyzers/security/actions.js +104 -0
  87. package/dist/analyzers/security/actions.js.map +1 -0
  88. package/dist/analyzers/security/detailed.d.ts +14 -0
  89. package/dist/analyzers/security/detailed.d.ts.map +1 -0
  90. package/dist/analyzers/security/detailed.js +124 -0
  91. package/dist/analyzers/security/detailed.js.map +1 -0
  92. package/dist/analyzers/security/gather.d.ts +12 -0
  93. package/dist/analyzers/security/gather.d.ts.map +1 -0
  94. package/dist/analyzers/security/gather.js +195 -0
  95. package/dist/analyzers/security/gather.js.map +1 -0
  96. package/dist/analyzers/security/index.d.ts +8 -0
  97. package/dist/analyzers/security/index.d.ts.map +1 -0
  98. package/dist/analyzers/security/index.js +173 -0
  99. package/dist/analyzers/security/index.js.map +1 -0
  100. package/dist/analyzers/security/scoring.d.ts +29 -0
  101. package/dist/analyzers/security/scoring.d.ts.map +1 -0
  102. package/dist/analyzers/security/scoring.js +40 -0
  103. package/dist/analyzers/security/scoring.js.map +1 -0
  104. package/dist/analyzers/security/shallow.d.ts +10 -0
  105. package/dist/analyzers/security/shallow.d.ts.map +1 -0
  106. package/dist/analyzers/security/shallow.js +8 -0
  107. package/dist/analyzers/security/shallow.js.map +1 -0
  108. package/dist/analyzers/security/types.d.ts +43 -0
  109. package/dist/analyzers/security/types.d.ts.map +1 -0
  110. package/dist/analyzers/security/types.js +6 -0
  111. package/dist/analyzers/security/types.js.map +1 -0
  112. package/dist/analyzers/tests/actions.d.ts +6 -0
  113. package/dist/analyzers/tests/actions.d.ts.map +1 -0
  114. package/dist/analyzers/tests/actions.js +80 -0
  115. package/dist/analyzers/tests/actions.js.map +1 -0
  116. package/dist/analyzers/tests/detailed.d.ts +14 -0
  117. package/dist/analyzers/tests/detailed.d.ts.map +1 -0
  118. package/dist/analyzers/tests/detailed.js +121 -0
  119. package/dist/analyzers/tests/detailed.js.map +1 -0
  120. package/dist/analyzers/tests/gather.d.ts +5 -0
  121. package/dist/analyzers/tests/gather.d.ts.map +1 -0
  122. package/dist/analyzers/tests/gather.js +270 -0
  123. package/dist/analyzers/tests/gather.js.map +1 -0
  124. package/dist/analyzers/tests/import-graph.d.ts +48 -0
  125. package/dist/analyzers/tests/import-graph.d.ts.map +1 -0
  126. package/dist/analyzers/tests/import-graph.js +231 -0
  127. package/dist/analyzers/tests/import-graph.js.map +1 -0
  128. package/dist/analyzers/tests/index.d.ts +8 -0
  129. package/dist/analyzers/tests/index.d.ts.map +1 -0
  130. package/dist/analyzers/tests/index.js +247 -0
  131. package/dist/analyzers/tests/index.js.map +1 -0
  132. package/dist/analyzers/tests/scoring.d.ts +27 -0
  133. package/dist/analyzers/tests/scoring.d.ts.map +1 -0
  134. package/dist/analyzers/tests/scoring.js +38 -0
  135. package/dist/analyzers/tests/scoring.js.map +1 -0
  136. package/dist/analyzers/tests/shallow.d.ts +9 -0
  137. package/dist/analyzers/tests/shallow.d.ts.map +1 -0
  138. package/dist/analyzers/tests/shallow.js +8 -0
  139. package/dist/analyzers/tests/shallow.js.map +1 -0
  140. package/dist/analyzers/tests/types.d.ts +49 -0
  141. package/dist/analyzers/tests/types.d.ts.map +1 -0
  142. package/dist/analyzers/tests/types.js +6 -0
  143. package/dist/analyzers/tests/types.js.map +1 -0
  144. package/dist/analyzers/tools/cloc.d.ts +8 -0
  145. package/dist/analyzers/tools/cloc.d.ts.map +1 -0
  146. package/dist/analyzers/tools/cloc.js +49 -0
  147. package/dist/analyzers/tools/cloc.js.map +1 -0
  148. package/dist/analyzers/tools/coverage.d.ts +59 -0
  149. package/dist/analyzers/tools/coverage.d.ts.map +1 -0
  150. package/dist/analyzers/tools/coverage.js +280 -0
  151. package/dist/analyzers/tools/coverage.js.map +1 -0
  152. package/dist/analyzers/tools/cvss-v4-lookup.d.ts +10 -0
  153. package/dist/analyzers/tools/cvss-v4-lookup.d.ts.map +1 -0
  154. package/dist/analyzers/tools/cvss-v4-lookup.js +284 -0
  155. package/dist/analyzers/tools/cvss-v4-lookup.js.map +1 -0
  156. package/dist/analyzers/tools/cvss-v4.d.ts +24 -0
  157. package/dist/analyzers/tools/cvss-v4.d.ts.map +1 -0
  158. package/dist/analyzers/tools/cvss-v4.js +362 -0
  159. package/dist/analyzers/tools/cvss-v4.js.map +1 -0
  160. package/dist/analyzers/tools/default-exclusions.gitignore +56 -0
  161. package/dist/analyzers/tools/exclusions.d.ts +70 -0
  162. package/dist/analyzers/tools/exclusions.d.ts.map +1 -0
  163. package/dist/analyzers/tools/exclusions.js +250 -0
  164. package/dist/analyzers/tools/exclusions.js.map +1 -0
  165. package/dist/analyzers/tools/generic.d.ts +4 -0
  166. package/dist/analyzers/tools/generic.d.ts.map +1 -0
  167. package/dist/analyzers/tools/generic.js +198 -0
  168. package/dist/analyzers/tools/generic.js.map +1 -0
  169. package/dist/analyzers/tools/gitleaks.d.ts +8 -0
  170. package/dist/analyzers/tools/gitleaks.d.ts.map +1 -0
  171. package/dist/analyzers/tools/gitleaks.js +58 -0
  172. package/dist/analyzers/tools/gitleaks.js.map +1 -0
  173. package/dist/analyzers/tools/graphify.d.ts +4 -0
  174. package/dist/analyzers/tools/graphify.d.ts.map +1 -0
  175. package/dist/analyzers/tools/graphify.js +222 -0
  176. package/dist/analyzers/tools/graphify.js.map +1 -0
  177. package/dist/analyzers/tools/osv.d.ts +51 -0
  178. package/dist/analyzers/tools/osv.d.ts.map +1 -0
  179. package/dist/analyzers/tools/osv.js +188 -0
  180. package/dist/analyzers/tools/osv.js.map +1 -0
  181. package/dist/analyzers/tools/parallel.d.ts +8 -0
  182. package/dist/analyzers/tools/parallel.d.ts.map +1 -0
  183. package/dist/analyzers/tools/parallel.js +195 -0
  184. package/dist/analyzers/tools/parallel.js.map +1 -0
  185. package/dist/analyzers/tools/runner.d.ts +13 -0
  186. package/dist/analyzers/tools/runner.d.ts.map +1 -0
  187. package/dist/analyzers/tools/runner.js +109 -0
  188. package/dist/analyzers/tools/runner.js.map +1 -0
  189. package/dist/analyzers/tools/suppressions.d.ts +55 -0
  190. package/dist/analyzers/tools/suppressions.d.ts.map +1 -0
  191. package/dist/analyzers/tools/suppressions.js +203 -0
  192. package/dist/analyzers/tools/suppressions.js.map +1 -0
  193. package/dist/analyzers/tools/timing.d.ts +9 -0
  194. package/dist/analyzers/tools/timing.d.ts.map +1 -0
  195. package/dist/analyzers/tools/timing.js +29 -0
  196. package/dist/analyzers/tools/timing.js.map +1 -0
  197. package/dist/analyzers/tools/tool-registry.d.ts +86 -0
  198. package/dist/analyzers/tools/tool-registry.d.ts.map +1 -0
  199. package/dist/analyzers/tools/tool-registry.js +705 -0
  200. package/dist/analyzers/tools/tool-registry.js.map +1 -0
  201. package/dist/analyzers/types.d.ts +125 -0
  202. package/dist/analyzers/types.d.ts.map +1 -0
  203. package/dist/analyzers/types.js +11 -0
  204. package/dist/analyzers/types.js.map +1 -0
  205. package/dist/cli.d.ts.map +1 -1
  206. package/dist/cli.js +405 -0
  207. package/dist/cli.js.map +1 -1
  208. package/dist/detect.d.ts.map +1 -1
  209. package/dist/detect.js +24 -15
  210. package/dist/detect.js.map +1 -1
  211. package/dist/languages/csharp.d.ts +5 -0
  212. package/dist/languages/csharp.d.ts.map +1 -0
  213. package/dist/languages/csharp.js +265 -0
  214. package/dist/languages/csharp.js.map +1 -0
  215. package/dist/languages/go.d.ts +11 -0
  216. package/dist/languages/go.d.ts.map +1 -0
  217. package/dist/languages/go.js +321 -0
  218. package/dist/languages/go.js.map +1 -0
  219. package/dist/languages/index.d.ts +6 -0
  220. package/dist/languages/index.d.ts.map +1 -0
  221. package/dist/languages/index.js +18 -0
  222. package/dist/languages/index.js.map +1 -0
  223. package/dist/languages/python.d.ts +3 -0
  224. package/dist/languages/python.d.ts.map +1 -0
  225. package/dist/languages/python.js +284 -0
  226. package/dist/languages/python.js.map +1 -0
  227. package/dist/languages/rust.d.ts +17 -0
  228. package/dist/languages/rust.d.ts.map +1 -0
  229. package/dist/languages/rust.js +333 -0
  230. package/dist/languages/rust.js.map +1 -0
  231. package/dist/languages/types.d.ts +38 -0
  232. package/dist/languages/types.d.ts.map +1 -0
  233. package/dist/languages/types.js +3 -0
  234. package/dist/languages/types.js.map +1 -0
  235. package/dist/languages/typescript.d.ts +15 -0
  236. package/dist/languages/typescript.d.ts.map +1 -0
  237. package/dist/languages/typescript.js +353 -0
  238. package/dist/languages/typescript.js.map +1 -0
  239. package/dist/logger.d.ts +1 -0
  240. package/dist/logger.d.ts.map +1 -1
  241. package/dist/logger.js +25 -12
  242. package/dist/logger.js.map +1 -1
  243. package/dist/project-yaml.d.ts.map +1 -1
  244. package/dist/project-yaml.js +1 -0
  245. package/dist/project-yaml.js.map +1 -1
  246. package/dist/tools-cli.d.ts +2 -0
  247. package/dist/tools-cli.d.ts.map +1 -0
  248. package/dist/tools-cli.js +231 -0
  249. package/dist/tools-cli.js.map +1 -0
  250. package/dist/types.d.ts +10 -0
  251. package/dist/types.d.ts.map +1 -1
  252. package/package.json +6 -2
  253. package/templates/.claude/commands/dev-report.md +34 -4
  254. package/templates/.claude/commands/health.md +45 -2
  255. package/templates/.claude/commands/quality.md.template +38 -15
  256. package/templates/.claude/commands/test-gaps.md +36 -2
  257. package/templates/.claude/commands/vulnerabilities.md +36 -2
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Import-graph test matching.
3
+ *
4
+ * Replaces the filename-based `matchTestsToSource` heuristic with something
5
+ * that actually reflects what a test exercises. A source file is "tested"
6
+ * when at least one active test file imports it, directly or transitively
7
+ * through a small number of hops.
8
+ *
9
+ * The point is to rescue common real-world shapes the filename matcher
10
+ * misses:
11
+ *
12
+ * test/cli-init.test.ts imports src/cli.ts
13
+ * src/cli.ts imports src/generator.ts, src/detect.ts, ...
14
+ * → all of those count as tested even though none of their filenames
15
+ * contain "cli-init".
16
+ *
17
+ * Scope: TS/JS + Python. Go, Rust, and C# are follow-ups; for now they
18
+ * fall back to the filename matcher.
19
+ *
20
+ * Implementation: read each candidate file, extract `import` / `from ...
21
+ * import` / `require(...)` module paths with regex, resolve each to a
22
+ * project-relative path, BFS up to `maxHops` from the test-file seed set.
23
+ * Node-style `node_modules` packages are treated as external and skipped.
24
+ */
25
+ export interface ImportGraphOptions {
26
+ /** Transitive depth. 0 = direct imports only. Default 3. */
27
+ maxHops?: number;
28
+ }
29
+ /**
30
+ * Build the set of source files reachable from the given test-file seeds by
31
+ * following import edges up to maxHops. Paths are project-relative.
32
+ */
33
+ export declare function buildReachable(seeds: string[], cwd: string, options?: ImportGraphOptions): Set<string>;
34
+ /** Raw module specifiers imported by the given file. External pkgs included. */
35
+ export declare function extractImports(relPath: string, cwd: string): string[];
36
+ /**
37
+ * TS / JS: capture quoted specifiers in import / require / dynamic import.
38
+ * Deliberately loose — we want recall, not parser fidelity.
39
+ */
40
+ export declare function extractTsJsImports(content: string): string[];
41
+ /** Python: `import foo.bar`, `from foo.bar import X`, `from .rel import X`. */
42
+ export declare function extractPyImports(content: string): string[];
43
+ /**
44
+ * Resolve a raw import specifier to a project-relative file path, or null
45
+ * if it's external or unresolvable.
46
+ */
47
+ export declare function resolveImport(fromFile: string, spec: string, cwd: string): string | null;
48
+ //# sourceMappingURL=import-graph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import-graph.d.ts","sourceRoot":"","sources":["../../../src/analyzers/tests/import-graph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAMH,MAAM,WAAW,kBAAkB;IACjC,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EAAE,EACf,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,kBAAuB,GAC/B,GAAG,CAAC,MAAM,CAAC,CAmBb;AAID,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAYrE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAsB5D;AAED,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAwB1D;AAID;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMxF"}
@@ -0,0 +1,231 @@
1
+ "use strict";
2
+ /**
3
+ * Import-graph test matching.
4
+ *
5
+ * Replaces the filename-based `matchTestsToSource` heuristic with something
6
+ * that actually reflects what a test exercises. A source file is "tested"
7
+ * when at least one active test file imports it, directly or transitively
8
+ * through a small number of hops.
9
+ *
10
+ * The point is to rescue common real-world shapes the filename matcher
11
+ * misses:
12
+ *
13
+ * test/cli-init.test.ts imports src/cli.ts
14
+ * src/cli.ts imports src/generator.ts, src/detect.ts, ...
15
+ * → all of those count as tested even though none of their filenames
16
+ * contain "cli-init".
17
+ *
18
+ * Scope: TS/JS + Python. Go, Rust, and C# are follow-ups; for now they
19
+ * fall back to the filename matcher.
20
+ *
21
+ * Implementation: read each candidate file, extract `import` / `from ...
22
+ * import` / `require(...)` module paths with regex, resolve each to a
23
+ * project-relative path, BFS up to `maxHops` from the test-file seed set.
24
+ * Node-style `node_modules` packages are treated as external and skipped.
25
+ */
26
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
27
+ if (k2 === undefined) k2 = k;
28
+ var desc = Object.getOwnPropertyDescriptor(m, k);
29
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
30
+ desc = { enumerable: true, get: function() { return m[k]; } };
31
+ }
32
+ Object.defineProperty(o, k2, desc);
33
+ }) : (function(o, m, k, k2) {
34
+ if (k2 === undefined) k2 = k;
35
+ o[k2] = m[k];
36
+ }));
37
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
38
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
39
+ }) : function(o, v) {
40
+ o["default"] = v;
41
+ });
42
+ var __importStar = (this && this.__importStar) || (function () {
43
+ var ownKeys = function(o) {
44
+ ownKeys = Object.getOwnPropertyNames || function (o) {
45
+ var ar = [];
46
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
47
+ return ar;
48
+ };
49
+ return ownKeys(o);
50
+ };
51
+ return function (mod) {
52
+ if (mod && mod.__esModule) return mod;
53
+ var result = {};
54
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
55
+ __setModuleDefault(result, mod);
56
+ return result;
57
+ };
58
+ })();
59
+ Object.defineProperty(exports, "__esModule", { value: true });
60
+ exports.buildReachable = buildReachable;
61
+ exports.extractImports = extractImports;
62
+ exports.extractTsJsImports = extractTsJsImports;
63
+ exports.extractPyImports = extractPyImports;
64
+ exports.resolveImport = resolveImport;
65
+ const fs = __importStar(require("fs"));
66
+ const path = __importStar(require("path"));
67
+ const languages_1 = require("../../languages");
68
+ const TS_JS_EXT = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'];
69
+ /**
70
+ * Build the set of source files reachable from the given test-file seeds by
71
+ * following import edges up to maxHops. Paths are project-relative.
72
+ */
73
+ function buildReachable(seeds, cwd, options = {}) {
74
+ const maxHops = options.maxHops ?? 3;
75
+ const reached = new Set();
76
+ let frontier = seeds.slice();
77
+ for (let hop = 0; hop <= maxHops && frontier.length > 0; hop++) {
78
+ const next = [];
79
+ for (const file of frontier) {
80
+ const imports = extractImports(file, cwd);
81
+ for (const raw of imports) {
82
+ const resolved = resolveImport(file, raw, cwd);
83
+ if (!resolved)
84
+ continue;
85
+ if (reached.has(resolved))
86
+ continue;
87
+ reached.add(resolved);
88
+ next.push(resolved);
89
+ }
90
+ }
91
+ frontier = next;
92
+ }
93
+ return reached;
94
+ }
95
+ // ─── Extraction ─────────────────────────────────────────────────────────────
96
+ /** Raw module specifiers imported by the given file. External pkgs included. */
97
+ function extractImports(relPath, cwd) {
98
+ let content;
99
+ try {
100
+ content = fs.readFileSync(path.join(cwd, relPath), 'utf-8');
101
+ }
102
+ catch {
103
+ return [];
104
+ }
105
+ // Dispatch through language registry first; fall back to TS/JS.
106
+ const ext = path.extname(relPath);
107
+ const lang = languages_1.LANGUAGES.find((l) => l.sourceExtensions.includes(ext));
108
+ if (lang?.extractImports)
109
+ return lang.extractImports(content);
110
+ return extractTsJsImports(content);
111
+ }
112
+ /**
113
+ * TS / JS: capture quoted specifiers in import / require / dynamic import.
114
+ * Deliberately loose — we want recall, not parser fidelity.
115
+ */
116
+ function extractTsJsImports(content) {
117
+ const out = [];
118
+ const stripped = stripTsJsComments(content);
119
+ // Static imports: `import X from '...'`, `import { a } from '...'`, or
120
+ // side-effect `import '...';`. Require whitespace after `import` so we
121
+ // don't misfire on `import.meta` or similar.
122
+ const importRe = /\bimport\s+(?:[^'";]*?from\s+)?['"]([^'"]+)['"]/g;
123
+ // Re-exports: `export * from '...'`, `export { x } from '...'` — these
124
+ // are dependency edges even though they're not literal imports.
125
+ const reexportRe = /\bexport\s+(?:[^'";]*?from\s+)['"]([^'"]+)['"]/g;
126
+ // Dynamic imports: import('...')
127
+ const dynRe = /\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
128
+ // CommonJS: require('...')
129
+ const reqRe = /\brequire\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
130
+ for (const re of [importRe, reexportRe, dynRe, reqRe]) {
131
+ let m;
132
+ while ((m = re.exec(stripped)) !== null) {
133
+ out.push(m[1]);
134
+ }
135
+ }
136
+ return out;
137
+ }
138
+ /** Python: `import foo.bar`, `from foo.bar import X`, `from .rel import X`. */
139
+ function extractPyImports(content) {
140
+ const out = [];
141
+ const lines = stripPyComments(content).split('\n');
142
+ for (const line of lines) {
143
+ const trimmed = line.trim();
144
+ // `from X import Y` — keep X (may be relative: leading dots)
145
+ const fromMatch = trimmed.match(/^from\s+([.\w]+)\s+import\s+/);
146
+ if (fromMatch) {
147
+ out.push(fromMatch[1]);
148
+ continue;
149
+ }
150
+ // `import X` / `import X as Y` / `import X, Y`
151
+ const impMatch = trimmed.match(/^import\s+(.+)$/);
152
+ if (impMatch) {
153
+ for (const part of impMatch[1].split(',')) {
154
+ const name = part
155
+ .trim()
156
+ .split(/\s+as\s+/)[0]
157
+ .trim();
158
+ if (name)
159
+ out.push(name);
160
+ }
161
+ }
162
+ }
163
+ return out;
164
+ }
165
+ // ─── Resolution ─────────────────────────────────────────────────────────────
166
+ /**
167
+ * Resolve a raw import specifier to a project-relative file path, or null
168
+ * if it's external or unresolvable.
169
+ */
170
+ function resolveImport(fromFile, spec, cwd) {
171
+ // Dispatch through language registry first; fall back to TS/JS.
172
+ const ext = path.extname(fromFile);
173
+ const lang = languages_1.LANGUAGES.find((l) => l.sourceExtensions.includes(ext));
174
+ if (lang?.resolveImport)
175
+ return lang.resolveImport(fromFile, spec, cwd);
176
+ return resolveTsJsImport(fromFile, spec, cwd);
177
+ }
178
+ function resolveTsJsImport(fromFile, spec, cwd) {
179
+ // Only relative paths are internal; everything else is a package.
180
+ if (!spec.startsWith('./') && !spec.startsWith('../'))
181
+ return null;
182
+ const fromDir = path.dirname(path.join(cwd, fromFile));
183
+ const baseAbs = path.resolve(fromDir, spec);
184
+ // Already has a supported extension?
185
+ for (const ext of TS_JS_EXT) {
186
+ if (baseAbs.endsWith(ext) && fileExists(baseAbs)) {
187
+ return toRel(baseAbs, cwd);
188
+ }
189
+ }
190
+ // Try appending each extension.
191
+ for (const ext of TS_JS_EXT) {
192
+ if (fileExists(baseAbs + ext))
193
+ return toRel(baseAbs + ext, cwd);
194
+ }
195
+ // Directory with index.* ?
196
+ for (const ext of TS_JS_EXT) {
197
+ const idx = path.join(baseAbs, 'index' + ext);
198
+ if (fileExists(idx))
199
+ return toRel(idx, cwd);
200
+ }
201
+ return null;
202
+ }
203
+ // ─── Helpers ────────────────────────────────────────────────────────────────
204
+ function fileExists(p) {
205
+ try {
206
+ return fs.statSync(p).isFile();
207
+ }
208
+ catch {
209
+ return false;
210
+ }
211
+ }
212
+ function toRel(abs, cwd) {
213
+ return path.relative(cwd, abs).replace(/\\/g, '/');
214
+ }
215
+ /**
216
+ * Strip `//` and `/* ... *\/` comments from TS/JS so commented-out imports
217
+ * don't count. Strings aren't parsed carefully — we accept rare false
218
+ * positives in code inside quoted strings; they won't resolve anyway.
219
+ */
220
+ function stripTsJsComments(src) {
221
+ // Block comments
222
+ let out = src.replace(/\/\*[\s\S]*?\*\//g, '');
223
+ // Line comments (don't eat #! shebangs mid-file — unlikely but cheap to keep)
224
+ out = out.replace(/(^|[^:"'/])\/\/[^\n]*/g, '$1');
225
+ return out;
226
+ }
227
+ /** Strip Python line comments (# ...). Leaves docstrings intact. */
228
+ function stripPyComments(src) {
229
+ return src.replace(/(^|[^\w"'])#[^\n]*/g, '$1');
230
+ }
231
+ //# sourceMappingURL=import-graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import-graph.js","sourceRoot":"","sources":["../../../src/analyzers/tests/import-graph.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { TestGapsReport } from './types';
2
+ export type { TestGapsReport, SourceFile, TestFile, CoverageSource } from './types';
3
+ export interface AnalyzeTestGapsOptions {
4
+ verbose?: boolean;
5
+ }
6
+ export declare function analyzeTestGaps(repoPath: string, options?: AnalyzeTestGapsOptions): TestGapsReport;
7
+ export declare function formatTestGapsReport(report: TestGapsReport, elapsed: string): string;
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/analyzers/tests/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,cAAc,EAA8B,MAAM,SAAS,CAAC;AAErE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAEpF,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,sBAA2B,GACnC,cAAc,CAwGhB;AAsBD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAqFpF"}
@@ -0,0 +1,247 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.analyzeTestGaps = analyzeTestGaps;
37
+ exports.formatTestGapsReport = formatTestGapsReport;
38
+ /**
39
+ * Test gap analyzer — public API.
40
+ */
41
+ const path = __importStar(require("path"));
42
+ const detect_1 = require("../../detect");
43
+ const runner_1 = require("../tools/runner");
44
+ const timing_1 = require("../tools/timing");
45
+ const coverage_1 = require("../tools/coverage");
46
+ const import_graph_1 = require("./import-graph");
47
+ const gather_1 = require("./gather");
48
+ function analyzeTestGaps(repoPath, options = {}) {
49
+ const verbose = !!options.verbose;
50
+ const stack = (0, detect_1.detect)(repoPath);
51
+ const toolsUsed = ['find', 'grep', 'git'];
52
+ const toolsUnavailable = [];
53
+ const testFiles = (0, timing_1.timed)('test-files', verbose, () => (0, gather_1.gatherTestFiles)(repoPath));
54
+ const sourceFiles = (0, timing_1.timed)('source-files', verbose, () => (0, gather_1.gatherSourceFiles)(repoPath));
55
+ (0, timing_1.timed)('match', verbose, () => (0, gather_1.matchTestsToSource)(testFiles, sourceFiles));
56
+ // Signal precedence for test coverage (strongest wins for files it covers):
57
+ //
58
+ // 1. Coverage artifact — authoritative for files it has data for. If V8
59
+ // says a file has 0 covered lines, the file is untested, even if the
60
+ // filename heuristic or an import edge would suggest otherwise.
61
+ // 2. Import-graph reachability — credits files V8 didn't see (configs,
62
+ // modules outside the coverage `include` glob).
63
+ // 3. Filename match (`matchTestsToSource` above) — last-resort heuristic
64
+ // for files neither V8 nor the import graph has an opinion on.
65
+ //
66
+ // The coverage step OVERRIDES the prior filename-match decision rather
67
+ // than ORing with it; otherwise files like `cli.ts` get falsely credited
68
+ // by basename similarity to `cli-init.test.ts`, even though V8 measured
69
+ // them at 0%.
70
+ const coverage = (0, timing_1.timed)('coverage', verbose, () => (0, coverage_1.loadCoverage)(repoPath));
71
+ if (coverage) {
72
+ toolsUsed.push(`coverage:${coverage.source}`);
73
+ for (const s of sourceFiles) {
74
+ const fc = coverage.files.get(s.path);
75
+ if (fc !== undefined) {
76
+ s.hasMatchingTest = fc.covered > 0;
77
+ }
78
+ // Files not in the artifact fall through to import-graph below.
79
+ }
80
+ }
81
+ // Import-graph: a source file reachable from any active test file via
82
+ // direct or transitive imports counts as tested. Skips files V8 already
83
+ // measured (their decision is authoritative); credits the rest.
84
+ const activeTestPaths = testFiles.filter((t) => t.status === 'active').map((t) => t.path);
85
+ const reached = (0, timing_1.timed)('import-graph', verbose, () => (0, import_graph_1.buildReachable)(activeTestPaths, repoPath));
86
+ const importGraphUsable = reached.size > 0;
87
+ if (importGraphUsable) {
88
+ for (const s of sourceFiles) {
89
+ const measuredByCoverage = coverage?.files.has(s.path) ?? false;
90
+ if (!measuredByCoverage && reached.has(s.path)) {
91
+ s.hasMatchingTest = true;
92
+ }
93
+ }
94
+ toolsUsed.push('import-graph');
95
+ }
96
+ const activeTests = testFiles.filter((t) => t.status === 'active');
97
+ const commentedOut = testFiles.filter((t) => t.status === 'commented-out');
98
+ const untested = sourceFiles.filter((s) => !s.hasMatchingTest);
99
+ const untestedByRisk = { critical: 0, high: 0, medium: 0, low: 0 };
100
+ for (const s of untested)
101
+ untestedByRisk[s.risk]++;
102
+ let coverageSource = 'filename-match';
103
+ let effectiveCoverage;
104
+ if (coverage) {
105
+ coverageSource = coverage.source;
106
+ effectiveCoverage = Math.round(coverage.linePercent);
107
+ }
108
+ else {
109
+ // No artifact — count the share of source files with any test signal.
110
+ // Label as 'import-graph' whenever we had usable import data, since it's
111
+ // the stronger signal (filename-match is a heuristic fallback).
112
+ if (importGraphUsable)
113
+ coverageSource = 'import-graph';
114
+ effectiveCoverage =
115
+ sourceFiles.length > 0
116
+ ? Math.round((sourceFiles.filter((s) => s.hasMatchingTest).length / sourceFiles.length) * 100)
117
+ : 0;
118
+ }
119
+ return {
120
+ repo: stack.projectName || path.basename(repoPath),
121
+ analyzedAt: new Date().toISOString(),
122
+ commitSha: (0, runner_1.run)('git rev-parse --short HEAD 2>/dev/null', repoPath),
123
+ branch: (0, runner_1.run)('git rev-parse --abbrev-ref HEAD 2>/dev/null', repoPath),
124
+ summary: {
125
+ testFiles: testFiles.length,
126
+ activeTestFiles: activeTests.length,
127
+ commentedOutFiles: commentedOut.length,
128
+ effectiveCoverage,
129
+ coverageSource,
130
+ coverageSourceFile: coverage?.sourceFile,
131
+ sourceFiles: sourceFiles.length,
132
+ untestedCritical: untestedByRisk.critical,
133
+ untestedHigh: untestedByRisk.high,
134
+ untestedMedium: untestedByRisk.medium,
135
+ untestedLow: untestedByRisk.low,
136
+ },
137
+ testFiles,
138
+ gaps: untested.sort((a, b) => {
139
+ const R = { critical: 0, high: 1, medium: 2, low: 3 };
140
+ if (R[a.risk] !== R[b.risk])
141
+ return R[a.risk] - R[b.risk];
142
+ return b.lines - a.lines; // largest first within same risk
143
+ }),
144
+ toolsUsed,
145
+ toolsUnavailable,
146
+ };
147
+ }
148
+ function coverageSourceLabel(source, file) {
149
+ switch (source) {
150
+ case 'filename-match':
151
+ return 'filename match — install coverage pipeline for line-level truth';
152
+ case 'import-graph':
153
+ return 'import-graph reachability — install coverage pipeline for line-level truth';
154
+ case 'istanbul-summary':
155
+ case 'istanbul-final':
156
+ return `from ${file ?? 'istanbul artifact'}`;
157
+ case 'coverage-py':
158
+ return `from ${file ?? 'coverage.py'}`;
159
+ case 'go':
160
+ return `from ${file ?? 'go coverprofile'}`;
161
+ case 'cobertura':
162
+ return `from ${file ?? 'cobertura.xml'}`;
163
+ case 'lcov':
164
+ return `from ${file ?? 'lcov.info'}`;
165
+ }
166
+ }
167
+ function formatTestGapsReport(report, elapsed) {
168
+ const L = [];
169
+ L.push('# Test Gap Analysis');
170
+ L.push('');
171
+ L.push(`**Date:** ${report.analyzedAt.slice(0, 10)}`);
172
+ L.push(`**Repository:** ${report.repo}`);
173
+ L.push(`**Branch:** ${report.branch} (${report.commitSha})`);
174
+ L.push('');
175
+ L.push('---');
176
+ L.push('');
177
+ // Executive summary
178
+ const s = report.summary;
179
+ L.push('## Executive Summary');
180
+ L.push('');
181
+ L.push('| Metric | Value |');
182
+ L.push('|--------|-------|');
183
+ L.push(`| Test files found | ${s.testFiles} |`);
184
+ L.push(`| Active test files | ${s.activeTestFiles} |`);
185
+ L.push(`| Commented-out test files | ${s.commentedOutFiles} |`);
186
+ L.push(`| Effective coverage | **${s.effectiveCoverage}%** (${coverageSourceLabel(s.coverageSource, s.coverageSourceFile)}) |`);
187
+ L.push(`| Source files | ${s.sourceFiles} |`);
188
+ L.push(`| Untested (CRITICAL) | ${s.untestedCritical} |`);
189
+ L.push(`| Untested (HIGH) | ${s.untestedHigh} |`);
190
+ L.push(`| Untested (MEDIUM) | ${s.untestedMedium} |`);
191
+ L.push(`| Untested (LOW) | ${s.untestedLow} |`);
192
+ L.push('');
193
+ L.push('---');
194
+ L.push('');
195
+ // Test inventory
196
+ L.push('## Test Inventory');
197
+ L.push('');
198
+ if (report.testFiles.length === 0) {
199
+ L.push('No test files found.');
200
+ }
201
+ else {
202
+ L.push('| File | Status | Framework |');
203
+ L.push('|------|--------|-----------|');
204
+ for (const t of report.testFiles) {
205
+ L.push(`| \`${t.path}\` | ${t.status.toUpperCase()} | ${t.framework || '-'} |`);
206
+ }
207
+ }
208
+ L.push('');
209
+ L.push('---');
210
+ L.push('');
211
+ // Gaps by risk tier
212
+ const tiers = [
213
+ { risk: 'critical', title: 'CRITICAL (Security/auth risk)' },
214
+ { risk: 'high', title: 'HIGH (Business logic/large files)' },
215
+ { risk: 'medium', title: 'MEDIUM (Standard controllers/services)' },
216
+ { risk: 'low', title: 'LOW (Models/utilities)' },
217
+ ];
218
+ L.push('## Critical Gaps');
219
+ L.push('');
220
+ for (const tier of tiers) {
221
+ const items = report.gaps.filter((g) => g.risk === tier.risk);
222
+ if (items.length === 0)
223
+ continue;
224
+ L.push(`### Priority: ${tier.title}`);
225
+ L.push('');
226
+ L.push('| File | Type | Lines | Risk |');
227
+ L.push('|------|------|-------|------|');
228
+ for (const g of items.slice(0, 30)) {
229
+ L.push(`| \`${g.path}\` | ${g.type} | ${g.lines} | ${g.risk.toUpperCase()} |`);
230
+ }
231
+ if (items.length > 30) {
232
+ L.push(`| ... | ... | ... | ${items.length - 30} more |`);
233
+ }
234
+ L.push('');
235
+ }
236
+ L.push('---');
237
+ L.push('');
238
+ L.push(`**Tools used:** ${report.toolsUsed.join(', ')}`);
239
+ if (report.toolsUnavailable.length > 0) {
240
+ L.push(`**Tools unavailable:** ${report.toolsUnavailable.join(', ')}`);
241
+ }
242
+ L.push(`**Analysis time:** ${elapsed}s`);
243
+ L.push('');
244
+ L.push('*Generated by [VyuhLabs DXKit](https://www.npmjs.com/package/@vyuhlabs/dxkit)*');
245
+ return L.join('\n');
246
+ }
247
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/analyzers/tests/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkBA,0CA2GC;AAsBD,oDAqFC;AAxOD;;GAEG;AACH,2CAA6B;AAC7B,yCAAsC;AACtC,4CAAsC;AACtC,4CAAwC;AACxC,gDAAiD;AACjD,iDAAgD;AAChD,qCAAkF;AASlF,SAAgB,eAAe,CAC7B,QAAgB,EAChB,UAAkC,EAAE;IAEpC,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;IAClC,MAAM,KAAK,GAAG,IAAA,eAAM,EAAC,QAAQ,CAAC,CAAC;IAC/B,MAAM,SAAS,GAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IACpD,MAAM,gBAAgB,GAAa,EAAE,CAAC;IAEtC,MAAM,SAAS,GAAG,IAAA,cAAK,EAAC,YAAY,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAA,wBAAe,EAAC,QAAQ,CAAC,CAAC,CAAC;IAChF,MAAM,WAAW,GAAG,IAAA,cAAK,EAAC,cAAc,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAA,0BAAiB,EAAC,QAAQ,CAAC,CAAC,CAAC;IACtF,IAAA,cAAK,EAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAA,2BAAkB,EAAC,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC;IAE1E,4EAA4E;IAC5E,EAAE;IACF,0EAA0E;IAC1E,0EAA0E;IAC1E,qEAAqE;IACrE,yEAAyE;IACzE,qDAAqD;IACrD,2EAA2E;IAC3E,oEAAoE;IACpE,EAAE;IACF,uEAAuE;IACvE,yEAAyE;IACzE,wEAAwE;IACxE,cAAc;IACd,MAAM,QAAQ,GAAG,IAAA,cAAK,EAAC,UAAU,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAA,uBAAY,EAAC,QAAQ,CAAC,CAAC,CAAC;IAC1E,IAAI,QAAQ,EAAE,CAAC;QACb,SAAS,CAAC,IAAI,CAAC,YAAY,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAC9C,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;YAC5B,MAAM,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACtC,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;gBACrB,CAAC,CAAC,eAAe,GAAG,EAAE,CAAC,OAAO,GAAG,CAAC,CAAC;YACrC,CAAC;YACD,gEAAgE;QAClE,CAAC;IACH,CAAC;IAED,sEAAsE;IACtE,wEAAwE;IACxE,gEAAgE;IAChE,MAAM,eAAe,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC1F,MAAM,OAAO,GAAG,IAAA,cAAK,EAAC,cAAc,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAA,6BAAc,EAAC,eAAe,EAAE,QAAQ,CAAC,CAAC,CAAC;IAChG,MAAM,iBAAiB,GAAG,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC;IAC3C,IAAI,iBAAiB,EAAE,CAAC;QACtB,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;YAC5B,MAAM,kBAAkB,GAAG,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC;YAChE,IAAI,CAAC,kBAAkB,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC/C,CAAC,CAAC,eAAe,GAAG,IAAI,CAAC;YAC3B,CAAC;QACH,CAAC;QACD,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACjC,CAAC;IAED,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC;IACnE,MAAM,YAAY,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,eAAe,CAAC,CAAC;IAC3E,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;IAE/D,MAAM,cAAc,GAAG,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;IACnE,KAAK,MAAM,CAAC,IAAI,QAAQ;QAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;IAEnD,IAAI,cAAc,GAAmB,gBAAgB,CAAC;IACtD,IAAI,iBAAyB,CAAC;IAC9B,IAAI,QAAQ,EAAE,CAAC;QACb,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC;QACjC,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACvD,CAAC;SAAM,CAAC;QACN,sEAAsE;QACtE,yEAAyE;QACzE,gEAAgE;QAChE,IAAI,iBAAiB;YAAE,cAAc,GAAG,cAAc,CAAC;QACvD,iBAAiB;YACf,WAAW,CAAC,MAAM,GAAG,CAAC;gBACpB,CAAC,CAAC,IAAI,CAAC,KAAK,CACR,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,GAAG,GAAG,CACjF;gBACH,CAAC,CAAC,CAAC,CAAC;IACV,CAAC;IAED,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,WAAW,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAClD,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACpC,SAAS,EAAE,IAAA,YAAG,EAAC,wCAAwC,EAAE,QAAQ,CAAC;QAClE,MAAM,EAAE,IAAA,YAAG,EAAC,6CAA6C,EAAE,QAAQ,CAAC;QACpE,OAAO,EAAE;YACP,SAAS,EAAE,SAAS,CAAC,MAAM;YAC3B,eAAe,EAAE,WAAW,CAAC,MAAM;YACnC,iBAAiB,EAAE,YAAY,CAAC,MAAM;YACtC,iBAAiB;YACjB,cAAc;YACd,kBAAkB,EAAE,QAAQ,EAAE,UAAU;YACxC,WAAW,EAAE,WAAW,CAAC,MAAM;YAC/B,gBAAgB,EAAE,cAAc,CAAC,QAAQ;YACzC,YAAY,EAAE,cAAc,CAAC,IAAI;YACjC,cAAc,EAAE,cAAc,CAAC,MAAM;YACrC,WAAW,EAAE,cAAc,CAAC,GAAG;SAChC;QACD,SAAS;QACT,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YAC3B,MAAM,CAAC,GAAG,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;YACtD,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;gBAAE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC1D,OAAO,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,iCAAiC;QAC7D,CAAC,CAAC;QACF,SAAS;QACT,gBAAgB;KACjB,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAAC,MAAsB,EAAE,IAAa;IAChE,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,gBAAgB;YACnB,OAAO,iEAAiE,CAAC;QAC3E,KAAK,cAAc;YACjB,OAAO,4EAA4E,CAAC;QACtF,KAAK,kBAAkB,CAAC;QACxB,KAAK,gBAAgB;YACnB,OAAO,QAAQ,IAAI,IAAI,mBAAmB,EAAE,CAAC;QAC/C,KAAK,aAAa;YAChB,OAAO,QAAQ,IAAI,IAAI,aAAa,EAAE,CAAC;QACzC,KAAK,IAAI;YACP,OAAO,QAAQ,IAAI,IAAI,iBAAiB,EAAE,CAAC;QAC7C,KAAK,WAAW;YACd,OAAO,QAAQ,IAAI,IAAI,eAAe,EAAE,CAAC;QAC3C,KAAK,MAAM;YACT,OAAO,QAAQ,IAAI,IAAI,WAAW,EAAE,CAAC;IACzC,CAAC;AACH,CAAC;AAED,SAAgB,oBAAoB,CAAC,MAAsB,EAAE,OAAe;IAC1E,MAAM,CAAC,GAAa,EAAE,CAAC;IACvB,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IAC9B,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACX,CAAC,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;IACtD,CAAC,CAAC,IAAI,CAAC,mBAAmB,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC,CAAC,IAAI,CAAC,eAAe,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,SAAS,GAAG,CAAC,CAAC;IAC7D,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACX,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACd,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEX,oBAAoB;IACpB,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC;IACzB,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IAC/B,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACX,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IAC7B,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IAC7B,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC;IAChD,CAAC,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC,eAAe,IAAI,CAAC,CAAC;IACvD,CAAC,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC,iBAAiB,IAAI,CAAC,CAAC;IAChE,CAAC,CAAC,IAAI,CACJ,4BAA4B,CAAC,CAAC,iBAAiB,QAAQ,mBAAmB,CAAC,CAAC,CAAC,cAAc,EAAE,CAAC,CAAC,kBAAkB,CAAC,KAAK,CACxH,CAAC;IACF,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC;IAC9C,CAAC,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC,gBAAgB,IAAI,CAAC,CAAC;IAC1D,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC;IAClD,CAAC,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC,cAAc,IAAI,CAAC,CAAC;IACtD,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC;IAChD,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACX,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACd,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEX,iBAAiB;IACjB,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAC5B,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACX,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClC,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACjC,CAAC;SAAM,CAAC;QACN,CAAC,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;QACxC,CAAC,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;QACxC,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACjC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,SAAS,IAAI,GAAG,IAAI,CAAC,CAAC;QAClF,CAAC;IACH,CAAC;IACD,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACX,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACd,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEX,oBAAoB;IACpB,MAAM,KAAK,GAAuD;QAChE,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,+BAA+B,EAAE;QAC5D,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,mCAAmC,EAAE;QAC5D,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,wCAAwC,EAAE;QACnE,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,wBAAwB,EAAE;KACjD,CAAC;IAEF,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC3B,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEX,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACjC,CAAC,CAAC,IAAI,CAAC,iBAAiB,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QACtC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACX,CAAC,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;QACzC,CAAC,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;QACzC,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;YACnC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QACjF,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YACtB,CAAC,CAAC,IAAI,CAAC,uBAAuB,KAAK,CAAC,MAAM,GAAG,EAAE,SAAS,CAAC,CAAC;QAC5D,CAAC;QACD,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACb,CAAC;IAED,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACd,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACX,CAAC,CAAC,IAAI,CAAC,mBAAmB,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzD,IAAI,MAAM,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvC,CAAC,CAAC,IAAI,CAAC,0BAA0B,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzE,CAAC;IACD,CAAC,CAAC,IAAI,CAAC,sBAAsB,OAAO,GAAG,CAAC,CAAC;IACzC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACX,CAAC,CAAC,IAAI,CAAC,gFAAgF,CAAC,CAAC;IACzF,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACtB,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Test-gap scoring — internal scorer over coverage counts, used for
3
+ * RemediationAction ranking in detailed reports.
4
+ */
5
+ export interface TestGapsCounts {
6
+ /** Untested source files by risk tier. */
7
+ untestedCritical: number;
8
+ untestedHigh: number;
9
+ untestedMedium: number;
10
+ untestedLow: number;
11
+ /** Source files that DO have active matching tests. */
12
+ testedSource: number;
13
+ /** Test files commented out entirely. */
14
+ commentedOutFiles: number;
15
+ }
16
+ /**
17
+ * 0-100 test-gap score from coverage per risk tier + commented-out penalty.
18
+ *
19
+ * Each tier contributes an independent sub-score proportional to the tier's
20
+ * coverage ratio. If a repo has no files in a tier, the tier is credited full.
21
+ * Tier weights (CRITICAL 30, HIGH 25, MEDIUM 20, LOW 15 = 90) leave 10 points
22
+ * that start as a baseline and get deducted by commented-out test files.
23
+ */
24
+ export declare function scoreTestGapsCounts(c: TestGapsCounts): {
25
+ score: number;
26
+ };
27
+ //# sourceMappingURL=scoring.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scoring.d.ts","sourceRoot":"","sources":["../../../src/analyzers/tests/scoring.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,cAAc;IAC7B,0CAA0C;IAC1C,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,YAAY,EAAE,MAAM,CAAC;IACrB,yCAAyC;IACzC,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,cAAc,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAuBxE"}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ /**
3
+ * Test-gap scoring — internal scorer over coverage counts, used for
4
+ * RemediationAction ranking in detailed reports.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.scoreTestGapsCounts = scoreTestGapsCounts;
8
+ /**
9
+ * 0-100 test-gap score from coverage per risk tier + commented-out penalty.
10
+ *
11
+ * Each tier contributes an independent sub-score proportional to the tier's
12
+ * coverage ratio. If a repo has no files in a tier, the tier is credited full.
13
+ * Tier weights (CRITICAL 30, HIGH 25, MEDIUM 20, LOW 15 = 90) leave 10 points
14
+ * that start as a baseline and get deducted by commented-out test files.
15
+ */
16
+ function scoreTestGapsCounts(c) {
17
+ function tierScore(untested, weight) {
18
+ // untestedSource in this tier vs. testedSource is not known — we approximate
19
+ // by treating all c.testedSource as spread proportionally. Simpler: credit
20
+ // weight based on how few untested remain relative to the *original* total
21
+ // for this tier. Since we don't have per-tier original totals after a patch,
22
+ // use a monotone formula: 1 / (1 + untested) gives perfect decay.
23
+ if (untested === 0)
24
+ return weight;
25
+ // Graceful curve: large untested → near 0, small untested → higher credit.
26
+ // This lets action patches (reducing untested) produce visible deltas.
27
+ return Math.round(weight / (1 + untested * 0.1));
28
+ }
29
+ let score = 10; // baseline credited to all repos with test infra
30
+ score += tierScore(c.untestedCritical, 30);
31
+ score += tierScore(c.untestedHigh, 25);
32
+ score += tierScore(c.untestedMedium, 20);
33
+ score += tierScore(c.untestedLow, 15);
34
+ // Commented-out tests signal atrophy.
35
+ score -= Math.min(c.commentedOutFiles * 5, 25);
36
+ return { score: Math.max(0, Math.min(100, score)) };
37
+ }
38
+ //# sourceMappingURL=scoring.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scoring.js","sourceRoot":"","sources":["../../../src/analyzers/tests/scoring.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAsBH,kDAuBC;AA/BD;;;;;;;GAOG;AACH,SAAgB,mBAAmB,CAAC,CAAiB;IACnD,SAAS,SAAS,CAAC,QAAgB,EAAE,MAAc;QACjD,6EAA6E;QAC7E,2EAA2E;QAC3E,2EAA2E;QAC3E,6EAA6E;QAC7E,kEAAkE;QAClE,IAAI,QAAQ,KAAK,CAAC;YAAE,OAAO,MAAM,CAAC;QAClC,2EAA2E;QAC3E,uEAAuE;QACvE,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,QAAQ,GAAG,GAAG,CAAC,CAAC,CAAC;IACnD,CAAC;IAED,IAAI,KAAK,GAAG,EAAE,CAAC,CAAC,iDAAiD;IACjE,KAAK,IAAI,SAAS,CAAC,CAAC,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;IAC3C,KAAK,IAAI,SAAS,CAAC,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IACvC,KAAK,IAAI,SAAS,CAAC,CAAC,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;IACzC,KAAK,IAAI,SAAS,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAEtC,sCAAsC;IACtC,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,iBAAiB,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;IAE/C,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;AACtD,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Testing dimension — shallow score for health aggregation.
3
+ *
4
+ * Phase 3: delegates to scoring.ts (identical behavior).
5
+ * Phase 5: will be replaced with dedicated test gap analyzer logic.
6
+ */
7
+ import { HealthMetrics, DimensionScore } from '../types';
8
+ export declare function scoreTestsDimension(m: HealthMetrics): DimensionScore;
9
+ //# sourceMappingURL=shallow.d.ts.map