evizi-kit 1.0.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 (201) hide show
  1. package/README.md +506 -0
  2. package/kits/agent/.agent/skills/claude-code-subagent-creator/SKILL.md +292 -0
  3. package/kits/agent/.agent/skills/claude-code-subagent-creator/references/claude-code-subagent-configuration.md +158 -0
  4. package/kits/agent/.agent/skills/claude-code-subagent-creator/templates/subagent-profile.template.md +26 -0
  5. package/kits/agent/.agent/skills/skill-creator/LICENSE.txt +202 -0
  6. package/kits/agent/.agent/skills/skill-creator/SKILL.md +485 -0
  7. package/kits/agent/.agent/skills/skill-creator/agents/analyzer.md +274 -0
  8. package/kits/agent/.agent/skills/skill-creator/agents/comparator.md +202 -0
  9. package/kits/agent/.agent/skills/skill-creator/agents/grader.md +223 -0
  10. package/kits/agent/.agent/skills/skill-creator/assets/eval_review.html +146 -0
  11. package/kits/agent/.agent/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  12. package/kits/agent/.agent/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  13. package/kits/agent/.agent/skills/skill-creator/references/schemas.md +430 -0
  14. package/kits/agent/.agent/skills/skill-creator/scripts/__init__.py +0 -0
  15. package/kits/agent/.agent/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  16. package/kits/agent/.agent/skills/skill-creator/scripts/generate_report.py +326 -0
  17. package/kits/agent/.agent/skills/skill-creator/scripts/improve_description.py +247 -0
  18. package/kits/agent/.agent/skills/skill-creator/scripts/package_skill.py +136 -0
  19. package/kits/agent/.agent/skills/skill-creator/scripts/quick_validate.py +103 -0
  20. package/kits/agent/.agent/skills/skill-creator/scripts/run_eval.py +310 -0
  21. package/kits/agent/.agent/skills/skill-creator/scripts/run_loop.py +328 -0
  22. package/kits/agent/.agent/skills/skill-creator/scripts/utils.py +47 -0
  23. package/kits/agent/manifest.json +10 -0
  24. package/kits/claude/.claude/agents/code-pusher.md +46 -0
  25. package/kits/claude/.claude/agents/feature-document-updater.md +37 -0
  26. package/kits/claude/.claude/agents/self-reviewer.md +32 -0
  27. package/kits/claude/.claude/agents/web-auto-agentic-workflow-initializer.md +42 -0
  28. package/kits/claude/.claude/agents/web-auto-assisted-fix-and-runner.md +36 -0
  29. package/kits/claude/.claude/agents/web-auto-chrome-devtools-selector-extractor.md +36 -0
  30. package/kits/claude/.claude/agents/web-auto-coder.md +33 -0
  31. package/kits/claude/.claude/agents/web-auto-fe-selector-extractor.md +31 -0
  32. package/kits/claude/.claude/agents/web-auto-fix-and-runner.md +35 -0
  33. package/kits/claude/.claude/agents/web-auto-lessons-learned-extractor.md +34 -0
  34. package/kits/claude/.claude/agents/web-auto-playwright-mcp-selector-extractor.md +37 -0
  35. package/kits/claude/.claude/agents/web-auto-source-instructions-updater.md +43 -0
  36. package/kits/claude/.claude/agents/web-auto-test-cases-generator.md +29 -0
  37. package/kits/claude/.claude/agents/web-auto-ticket-designer.md +35 -0
  38. package/kits/claude/.claude/agents/web-auto-ticket-playbook-planner.md +36 -0
  39. package/kits/claude/.claude/agents/web-auto.md +382 -0
  40. package/kits/claude/.claude/skills/claude-code-subagent-creator/SKILL.md +292 -0
  41. package/kits/claude/.claude/skills/claude-code-subagent-creator/references/claude-code-subagent-configuration.md +158 -0
  42. package/kits/claude/.claude/skills/claude-code-subagent-creator/templates/subagent-profile.template.md +26 -0
  43. package/kits/claude/.claude/skills/skill-creator/LICENSE.txt +202 -0
  44. package/kits/claude/.claude/skills/skill-creator/SKILL.md +485 -0
  45. package/kits/claude/.claude/skills/skill-creator/agents/analyzer.md +274 -0
  46. package/kits/claude/.claude/skills/skill-creator/agents/comparator.md +202 -0
  47. package/kits/claude/.claude/skills/skill-creator/agents/grader.md +223 -0
  48. package/kits/claude/.claude/skills/skill-creator/assets/eval_review.html +146 -0
  49. package/kits/claude/.claude/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  50. package/kits/claude/.claude/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  51. package/kits/claude/.claude/skills/skill-creator/references/schemas.md +430 -0
  52. package/kits/claude/.claude/skills/skill-creator/scripts/__init__.py +0 -0
  53. package/kits/claude/.claude/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  54. package/kits/claude/.claude/skills/skill-creator/scripts/generate_report.py +326 -0
  55. package/kits/claude/.claude/skills/skill-creator/scripts/improve_description.py +247 -0
  56. package/kits/claude/.claude/skills/skill-creator/scripts/package_skill.py +136 -0
  57. package/kits/claude/.claude/skills/skill-creator/scripts/quick_validate.py +103 -0
  58. package/kits/claude/.claude/skills/skill-creator/scripts/run_eval.py +310 -0
  59. package/kits/claude/.claude/skills/skill-creator/scripts/run_loop.py +328 -0
  60. package/kits/claude/.claude/skills/skill-creator/scripts/utils.py +47 -0
  61. package/kits/claude/manifest.json +10 -0
  62. package/kits/cursor/.cursor/agents/code-pusher.agent.md +43 -0
  63. package/kits/cursor/.cursor/agents/feature-document-updater.agent.md +34 -0
  64. package/kits/cursor/.cursor/agents/self-reviewer.agent.md +29 -0
  65. package/kits/cursor/.cursor/agents/web-auto-agentic-workflow-initializer.agent.md +37 -0
  66. package/kits/cursor/.cursor/agents/web-auto-assisted-fix-and-runner.agent.md +33 -0
  67. package/kits/cursor/.cursor/agents/web-auto-chrome-devtools-selector-extractor.agent.md +31 -0
  68. package/kits/cursor/.cursor/agents/web-auto-coder.agent.md +30 -0
  69. package/kits/cursor/.cursor/agents/web-auto-fe-selector-extractor.agent.md +28 -0
  70. package/kits/cursor/.cursor/agents/web-auto-fix-and-runner.agent.md +32 -0
  71. package/kits/cursor/.cursor/agents/web-auto-lessons-learned-extractor.agent.md +31 -0
  72. package/kits/cursor/.cursor/agents/web-auto-playwright-mcp-selector-extractor.agent.md +32 -0
  73. package/kits/cursor/.cursor/agents/web-auto-source-instructions-updater.agent.md +40 -0
  74. package/kits/cursor/.cursor/agents/web-auto-test-cases-generator.agent.md +26 -0
  75. package/kits/cursor/.cursor/agents/web-auto-ticket-designer.agent.md +32 -0
  76. package/kits/cursor/.cursor/agents/web-auto-ticket-playbook-planner.agent.md +33 -0
  77. package/kits/cursor/.cursor/agents/web-auto.agent.md +379 -0
  78. package/kits/cursor/.cursor/skills/claude-code-subagent-creator/SKILL.md +292 -0
  79. package/kits/cursor/.cursor/skills/claude-code-subagent-creator/references/claude-code-subagent-configuration.md +158 -0
  80. package/kits/cursor/.cursor/skills/claude-code-subagent-creator/templates/subagent-profile.template.md +26 -0
  81. package/kits/cursor/.cursor/skills/skill-creator/LICENSE.txt +202 -0
  82. package/kits/cursor/.cursor/skills/skill-creator/SKILL.md +485 -0
  83. package/kits/cursor/.cursor/skills/skill-creator/agents/analyzer.md +274 -0
  84. package/kits/cursor/.cursor/skills/skill-creator/agents/comparator.md +202 -0
  85. package/kits/cursor/.cursor/skills/skill-creator/agents/grader.md +223 -0
  86. package/kits/cursor/.cursor/skills/skill-creator/assets/eval_review.html +146 -0
  87. package/kits/cursor/.cursor/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  88. package/kits/cursor/.cursor/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  89. package/kits/cursor/.cursor/skills/skill-creator/references/schemas.md +430 -0
  90. package/kits/cursor/.cursor/skills/skill-creator/scripts/__init__.py +0 -0
  91. package/kits/cursor/.cursor/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  92. package/kits/cursor/.cursor/skills/skill-creator/scripts/generate_report.py +326 -0
  93. package/kits/cursor/.cursor/skills/skill-creator/scripts/improve_description.py +247 -0
  94. package/kits/cursor/.cursor/skills/skill-creator/scripts/package_skill.py +136 -0
  95. package/kits/cursor/.cursor/skills/skill-creator/scripts/quick_validate.py +103 -0
  96. package/kits/cursor/.cursor/skills/skill-creator/scripts/run_eval.py +310 -0
  97. package/kits/cursor/.cursor/skills/skill-creator/scripts/run_loop.py +328 -0
  98. package/kits/cursor/.cursor/skills/skill-creator/scripts/utils.py +47 -0
  99. package/kits/cursor/manifest.json +10 -0
  100. package/kits/github/.github/agents/code-pusher.agent.md +45 -0
  101. package/kits/github/.github/agents/feature-document-updater.agent.md +36 -0
  102. package/kits/github/.github/agents/self-reviewer.agent.md +31 -0
  103. package/kits/github/.github/agents/web-auto-agentic-workflow-initializer.agent.md +39 -0
  104. package/kits/github/.github/agents/web-auto-assisted-fix-and-runner.agent.md +35 -0
  105. package/kits/github/.github/agents/web-auto-chrome-devtools-selector-extractor.agent.md +33 -0
  106. package/kits/github/.github/agents/web-auto-coder.agent.md +32 -0
  107. package/kits/github/.github/agents/web-auto-fe-selector-extractor.agent.md +30 -0
  108. package/kits/github/.github/agents/web-auto-fix-and-runner.agent.md +34 -0
  109. package/kits/github/.github/agents/web-auto-lessons-learned-extractor.agent.md +33 -0
  110. package/kits/github/.github/agents/web-auto-playwright-mcp-selector-extractor.agent.md +34 -0
  111. package/kits/github/.github/agents/web-auto-source-instructions-updater.agent.md +42 -0
  112. package/kits/github/.github/agents/web-auto-test-cases-generator.agent.md +28 -0
  113. package/kits/github/.github/agents/web-auto-ticket-designer.agent.md +34 -0
  114. package/kits/github/.github/agents/web-auto-ticket-playbook-creator.agent.md +35 -0
  115. package/kits/github/.github/agents/web-auto.agent.md +382 -0
  116. package/kits/github/.github/skills/claude-code-subagent-creator/SKILL.md +310 -0
  117. package/kits/github/.github/skills/claude-code-subagent-creator/references/claude-code-subagent-configuration.md +158 -0
  118. package/kits/github/.github/skills/claude-code-subagent-creator/templates/subagent-profile.template.md +37 -0
  119. package/kits/github/.github/skills/skill-creator/LICENSE.txt +202 -0
  120. package/kits/github/.github/skills/skill-creator/SKILL.md +485 -0
  121. package/kits/github/.github/skills/skill-creator/agents/analyzer.md +274 -0
  122. package/kits/github/.github/skills/skill-creator/agents/comparator.md +202 -0
  123. package/kits/github/.github/skills/skill-creator/agents/grader.md +223 -0
  124. package/kits/github/.github/skills/skill-creator/assets/eval_review.html +146 -0
  125. package/kits/github/.github/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  126. package/kits/github/.github/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  127. package/kits/github/.github/skills/skill-creator/references/schemas.md +430 -0
  128. package/kits/github/.github/skills/skill-creator/scripts/__init__.py +0 -0
  129. package/kits/github/.github/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  130. package/kits/github/.github/skills/skill-creator/scripts/generate_report.py +326 -0
  131. package/kits/github/.github/skills/skill-creator/scripts/improve_description.py +247 -0
  132. package/kits/github/.github/skills/skill-creator/scripts/package_skill.py +136 -0
  133. package/kits/github/.github/skills/skill-creator/scripts/quick_validate.py +103 -0
  134. package/kits/github/.github/skills/skill-creator/scripts/run_eval.py +310 -0
  135. package/kits/github/.github/skills/skill-creator/scripts/run_loop.py +328 -0
  136. package/kits/github/.github/skills/skill-creator/scripts/utils.py +47 -0
  137. package/kits/github/manifest.json +10 -0
  138. package/kits/shared/docs/ai-code-review.md +440 -0
  139. package/kits/shared/docs/increase-unit-test-coverage.md +77 -0
  140. package/kits/shared/docs/pr-review-agent.md +501 -0
  141. package/kits/shared/docs/self-review-agent.md +246 -0
  142. package/kits/shared/docs/web-auto-agentic-workflow.md +506 -0
  143. package/kits/shared/manifest.json +11 -0
  144. package/kits/shared/skills/fix-automation-tests/SKILL.md +280 -0
  145. package/kits/shared/skills/fix-automation-tests/scripts/fetch_pr_changes.py +300 -0
  146. package/kits/shared/skills/fix-automation-tests/templates/impact-report.template.md +42 -0
  147. package/kits/shared/skills/increase-unit-test-coverage/SKILL.md +117 -0
  148. package/kits/shared/skills/increase-unit-test-coverage/scripts/filter_low_coverage.py +447 -0
  149. package/kits/shared/skills/pr-review/SKILL.md +200 -0
  150. package/kits/shared/skills/pr-review/references/automation.md +62 -0
  151. package/kits/shared/skills/pr-review/references/backend.md +95 -0
  152. package/kits/shared/skills/pr-review/references/frontend.md +103 -0
  153. package/kits/shared/skills/pr-review/references/mobile.md +108 -0
  154. package/kits/shared/skills/pr-review/references/output-schema.md +130 -0
  155. package/kits/shared/skills/pr-review/scripts/post-review.py +1395 -0
  156. package/kits/shared/skills/push-code/SKILL.md +176 -0
  157. package/kits/shared/skills/self-review/SKILL.md +234 -0
  158. package/kits/shared/skills/self-review/evals/evals.json +23 -0
  159. package/kits/shared/skills/self-review/references/automation.md +62 -0
  160. package/kits/shared/skills/self-review/references/backend.md +95 -0
  161. package/kits/shared/skills/self-review/references/frontend.md +103 -0
  162. package/kits/shared/skills/self-review/references/mobile.md +108 -0
  163. package/kits/shared/skills/self-review/templates/issues.template.md +72 -0
  164. package/kits/shared/skills/update-feature-document/SKILL.md +156 -0
  165. package/kits/shared/skills/update-feature-document/templates/delta.template.yaml +58 -0
  166. package/kits/shared/skills/update-feature-document/templates/feature.template.md +25 -0
  167. package/kits/shared/skills/web-auto-assisted-fix-and-run/SKILL.md +130 -0
  168. package/kits/shared/skills/web-auto-assisted-fix-and-run/references/resolve-api-error.md +108 -0
  169. package/kits/shared/skills/web-auto-assisted-fix-and-run/references/resolve-selector.md +60 -0
  170. package/kits/shared/skills/web-auto-assisted-fix-and-run/templates/issues-resolution-report-append.template.md +54 -0
  171. package/kits/shared/skills/web-auto-chrome-devtools-mcp-extract-selectors/SKILL.md +284 -0
  172. package/kits/shared/skills/web-auto-coding/SKILL.md +152 -0
  173. package/kits/shared/skills/web-auto-extract-lessons-learned/SKILL.md +168 -0
  174. package/kits/shared/skills/web-auto-extract-lessons-learned/templates/lessons-learned.template.md +115 -0
  175. package/kits/shared/skills/web-auto-fe-extract-selectors/SKILL.md +282 -0
  176. package/kits/shared/skills/web-auto-fe-extract-selectors/evals/evals.json +23 -0
  177. package/kits/shared/skills/web-auto-fix-and-run-test/SKILL.md +183 -0
  178. package/kits/shared/skills/web-auto-fix-and-run-test/templates/issues-resolution-report.template.md +77 -0
  179. package/kits/shared/skills/web-auto-generate-best-practices/SKILL.md +123 -0
  180. package/kits/shared/skills/web-auto-generate-instructions/SKILL.md +200 -0
  181. package/kits/shared/skills/web-auto-generate-instructions/evals/evals.json +23 -0
  182. package/kits/shared/skills/web-auto-generate-instructions/references/analysis-guide.md +145 -0
  183. package/kits/shared/skills/web-auto-generate-instructions/templates/web-auto-instructions.template.md +184 -0
  184. package/kits/shared/skills/web-auto-generate-project-blueprint/SKILL.md +181 -0
  185. package/kits/shared/skills/web-auto-generate-project-blueprint/evals/evals.json +57 -0
  186. package/kits/shared/skills/web-auto-generate-project-blueprint/templates/web-auto-project-blueprint.template.md +161 -0
  187. package/kits/shared/skills/web-auto-playwright-mcp-extract-selectors/SKILL.md +293 -0
  188. package/kits/shared/skills/web-auto-test-cases/SKILL.md +138 -0
  189. package/kits/shared/skills/web-auto-test-cases/evals/evals.json +129 -0
  190. package/kits/shared/skills/web-auto-test-cases/templates/test-cases.template.md +53 -0
  191. package/kits/shared/skills/web-auto-ticket-design/SKILL.md +199 -0
  192. package/kits/shared/skills/web-auto-ticket-design/templates/ticket-design.template.md +138 -0
  193. package/kits/shared/skills/web-auto-ticket-playbook/SKILL.md +218 -0
  194. package/kits/shared/skills/web-auto-ticket-playbook/evals/evals.json +23 -0
  195. package/kits/shared/skills/web-auto-ticket-playbook/templates/ticket-playbook.template.md +148 -0
  196. package/kits/shared/skills/web-auto-update-source-instructions/SKILL.md +156 -0
  197. package/kits/shared/skills/web-auto-update-source-instructions/evals/evals.json +22 -0
  198. package/kits/shared/skills/workspace-ai-nav-creator/SKILL.md +168 -0
  199. package/kits/shared/skills/workspace-ai-nav-creator/templates/agents-md.template.md +112 -0
  200. package/kits/shared/skills/workspace-ai-nav-creator/templates/claude-md.template.md +86 -0
  201. package/package.json +16 -0
@@ -0,0 +1,117 @@
1
+ ---
2
+ name: increase-unit-test-coverage
3
+ description: Systematically increase unit test coverage for files with low coverage. Works with any repository by reading configuration from `.documents-design/project.config.json`. Use this skill whenever the user mentions test coverage, coverage reports, low coverage percentages, writing unit tests for uncovered files, improving test quality, or anything related to test coverage improvement — even if they don't explicitly say "coverage skill". Also triggers for requests like "write tests for files that need them", "get coverage above 90%", or "which files need more tests".
4
+ ---
5
+
6
+ # Increase Unit Test Coverage
7
+
8
+ Improve unit test coverage by analyzing coverage reports and writing targeted, meaningful tests for under-covered files. The goal is not just hitting a coverage number — it's building confidence that the code works correctly. Coverage is the measurement tool, but test quality is the real objective.
9
+
10
+ ## Configuration
11
+
12
+ All project-specific settings live in `.documents-design/project.config.json` under the `unitTestCoverage` key. Read this file first — it tells you how the project runs tests, where test files live, what to exclude, and what framework/library to use.
13
+
14
+ Key fields: `testCommand`, `coverageReportPath`, `threshold`, `testDirectory`, `testFilePattern`, `testingFramework`, `testingLibrary`, `excludePatterns`, `excludeDirectories`, `reportTopN`.
15
+
16
+ If the config file doesn't exist, ask the user for these details before proceeding.
17
+
18
+ ## Workflow
19
+
20
+ ### Step 1: Generate a Coverage Baseline
21
+
22
+ Run the project's test command with coverage enabled (from `config.testCommand`):
23
+
24
+ ```bash
25
+ npm test -- --coverage
26
+ ```
27
+
28
+ This produces a `coverage-summary.json` at the path specified in `config.coverageReportPath`.
29
+
30
+ ### Step 2: Filter and Prioritize Files
31
+
32
+ Run the bundled filter script. It reads config automatically and produces a concise, prioritized markdown report:
33
+
34
+ ```bash
35
+ python <this-skill-directory>/scripts/filter_low_coverage.py
36
+ ```
37
+
38
+ CLI overrides are available (`-t` for threshold, `-n` for top-N files, `--full` for all files) but defaults from config are usually sufficient.
39
+
40
+ Read the generated report (at `config.outputReportPath`, default `low-coverage-report.md`). It groups files by severity:
41
+ - **šŸ”“ Zero** (0%) — no tests at all
42
+ - **🟠 Critical** (<50%) — severely under-tested
43
+ - **🟔 Moderate** (50–70%) — partial coverage with significant gaps
44
+ - **🟢 Minor** (70–threshold%) — close but missing edge cases
45
+
46
+ ### Step 3: Understand Before Writing
47
+
48
+ For each target file, do this research before writing a single test:
49
+
50
+ 1. **Read the source file thoroughly.** Understand what the module does, its public API, its side effects, and its dependencies. You cannot write a good test for code you don't understand.
51
+
52
+ 2. **Read existing tests** (if any) in `config.testDirectory`. Understand what's already covered and what patterns the project follows — naming conventions, file structure, import style, mocking approach. Consistency with existing tests matters because it reduces cognitive load for the team maintaining them.
53
+
54
+ 3. **Identify the coverage gaps.** Look at which lines, branches, and functions are uncovered. The coverage report tells you *where* the gaps are; the source code tells you *why* they matter. Focus on:
55
+ - Untested public functions and methods
56
+ - Conditional branches (if/else, switch, ternary) — these are where bugs hide
57
+ - Error handling paths — often the most impactful to test because failures in error handling cascade
58
+ - Edge cases at boundaries (empty arrays, null inputs, zero values)
59
+
60
+ ### Step 4: Write Meaningful Tests
61
+
62
+ Work through files in priority order (šŸ”“ → 🟠 → 🟔 → 🟢).
63
+
64
+ **What makes a test meaningful:**
65
+
66
+ - **Test behavior, not implementation.** Ask "what should this code *do*?" not "what lines does it execute?" A test that verifies the right output for a given input survives refactoring. A test that asserts internal method calls breaks when you clean up the code.
67
+
68
+ - **Each test should have a clear reason to exist.** If you can't explain what bug this test would catch, it's probably not worth writing. Coverage-gaming tests (calling a function without asserting anything meaningful) create a false sense of security.
69
+
70
+ - **Mock at the boundary, not everywhere.** Mock external dependencies (API calls, database, file system, third-party services) but avoid mocking internal modules unless absolutely necessary. Over-mocking makes tests brittle and disconnected from reality.
71
+
72
+ - **Name tests descriptively.** `it('should return empty array when user has no orders')` is far more useful than `it('test case 3')`. When a test fails, the name should immediately tell you what broke.
73
+
74
+ **For zero-coverage files:** Create a new test file following `config.testFilePattern` and `config.testingFramework`. Start with the happy path, then add error cases and edge cases.
75
+
76
+ **For partially-covered files:** Add targeted tests for the specific uncovered branches and functions. Don't rewrite existing tests unless they're broken.
77
+
78
+ ### Step 5: Verify and Iterate
79
+
80
+ After writing tests for each file:
81
+
82
+ 1. **Run tests** to confirm they pass:
83
+ ```bash
84
+ npm test
85
+ ```
86
+
87
+ 2. **Re-run coverage** to verify improvement:
88
+ ```bash
89
+ npm test -- --coverage
90
+ ```
91
+
92
+ 3. **Re-run the filter script** to update the report and see progress:
93
+ ```bash
94
+ python <this-skill-directory>/scripts/filter_low_coverage.py
95
+ ```
96
+
97
+ 4. If coverage didn't improve as expected, check whether your tests are actually exercising the uncovered paths. A common mistake is testing a wrapper function without triggering the branch inside it.
98
+
99
+ 5. **Move to the next file** in priority order. Complete one file fully before starting the next — partial work across many files is harder to track and review.
100
+
101
+ Continue until the target threshold from config is met, or until remaining uncovered files have legitimate reasons for lower coverage (pure type files, generated code, etc.).
102
+
103
+ ## Handling Tricky Scenarios
104
+
105
+ - **Heavily coupled code** that's hard to test often signals a design issue. Write the best tests you can, but note to the user that the module may benefit from refactoring to improve testability.
106
+ - **Files with complex state management** (Redux stores, context providers): test the logic separately from the UI. Reducer tests and selector tests are simpler and more valuable than full integration tests.
107
+ - **Async code**: make sure tests properly await promises and handle both resolved and rejected paths.
108
+ - **Files the config excludes** (CSS modules, type definitions, constants): skip these. They're excluded for a reason — they either can't be meaningfully tested or the effort isn't worth it.
109
+
110
+ ## Communicating Progress
111
+
112
+ When working through multiple files, provide brief updates after each file:
113
+ - What file was improved
114
+ - Coverage before → after
115
+ - Any issues encountered
116
+
117
+ This helps the user track progress and decide when to stop.
@@ -0,0 +1,447 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Filter files with low test coverage from Jest coverage-summary.json report.
4
+ Generates a markdown report of files that need increased test coverage.
5
+
6
+ Works with multiple IDEs - automatically detects configuration from:
7
+ - .documents-design/project.config.json
8
+
9
+ Automatically excludes files that don't need unit tests:
10
+ - CSS files: *.css.ts, *.css.tsx, *.css, *.less, *.scss, *.sass
11
+ - Configuration files: constants.ts, urls.ts, languageList.ts, countryList.ts,
12
+ countryPhoneCodes.ts, gdpr.ts, selectedColumns.ts
13
+ - Type definition files: types.ts, interfaces.ts
14
+ - Model files: Files in models/ directory (pure TypeScript interfaces)
15
+
16
+ All exclusion patterns can be customized via .documents-design/project.config.json
17
+ """
18
+
19
+ import json
20
+ import sys
21
+ import os
22
+ from pathlib import Path
23
+ from typing import Dict, List, Tuple, Optional
24
+
25
+
26
+ def find_config_file() -> Optional[Path]:
27
+ """
28
+ Find project.config.json in .documents-design/ directory.
29
+
30
+ Searches in order:
31
+ 1. .documents-design/project.config.json
32
+
33
+ Searches up to 5 directory levels from current location.
34
+ """
35
+ search_paths = [
36
+ '.documents-design/project.config.json',
37
+ ]
38
+
39
+ current = Path.cwd()
40
+ for _ in range(5): # Search up to 5 levels
41
+ for search_path in search_paths:
42
+ config_path = current / search_path
43
+ if config_path.exists():
44
+ return config_path
45
+ current = current.parent
46
+
47
+ return None
48
+
49
+
50
+ def find_skill_directory() -> str:
51
+ """
52
+ Find the skills directory based on IDE configuration.
53
+
54
+ Returns the appropriate path prefix:
55
+ - .cursor/skills/ (Cursor)
56
+ - .github/copilot/skills/ (GitHub Copilot)
57
+ - .agent/skills/ (Generic/Antigravity)
58
+ - .claude/skills/ (Claude Code)
59
+ """
60
+ current = Path.cwd()
61
+ for _ in range(5):
62
+ if (current / '.documents-design').exists():
63
+ return '.documents-design/'
64
+ current = current.parent
65
+
66
+ return '.documents-design/' # Default fallback
67
+
68
+
69
+ def load_project_config(workspace_root: Optional[str] = None) -> Optional[Dict]:
70
+ """Load configuration from project.config.json if it exists."""
71
+ if workspace_root:
72
+ # Try .documents-design location in workspace root
73
+ search_paths = [
74
+ Path(workspace_root) / '.documents-design' / 'project.config.json',
75
+ ]
76
+
77
+ config_path = None
78
+ for path in search_paths:
79
+ if path.exists():
80
+ config_path = path
81
+ break
82
+
83
+ if not config_path:
84
+ return None
85
+ else:
86
+ # Auto-detect config file
87
+ config_path = find_config_file()
88
+ if not config_path:
89
+ return None
90
+
91
+ try:
92
+ with open(config_path, 'r') as f:
93
+ config = json.load(f)
94
+ return config.get('unitTestCoverage', {})
95
+ except (json.JSONDecodeError, IOError):
96
+ return None
97
+
98
+
99
+ def load_coverage_report(report_path: str) -> Dict:
100
+ """Load the Jest coverage-summary.json file."""
101
+ try:
102
+ with open(report_path, 'r') as f:
103
+ return json.load(f)
104
+ except FileNotFoundError:
105
+ print(f"Error: Coverage report not found at {report_path}", file=sys.stderr)
106
+ sys.exit(1)
107
+ except json.JSONDecodeError as e:
108
+ print(f"Error: Invalid JSON in coverage report: {e}", file=sys.stderr)
109
+ sys.exit(1)
110
+
111
+
112
+ def calculate_average_coverage(file_data: Dict) -> float:
113
+ """Calculate average coverage percentage across all metrics."""
114
+ metrics = ['lines', 'statements', 'functions', 'branches']
115
+ percentages = [file_data.get(metric, {}).get('pct', 0) for metric in metrics]
116
+ return sum(percentages) / len(percentages)
117
+
118
+
119
+ def filter_low_coverage_files(
120
+ coverage_data: Dict,
121
+ threshold: float = 90.0,
122
+ workspace_root: str = None,
123
+ exclude_patterns: List[str] = None,
124
+ exclude_directories: List[str] = None
125
+ ) -> List[Tuple[str, Dict]]:
126
+ """
127
+ Filter files with coverage below threshold.
128
+
129
+ Args:
130
+ coverage_data: Coverage data from coverage-summary.json
131
+ threshold: Coverage percentage threshold (default 90%)
132
+ workspace_root: Workspace root path to make file paths relative
133
+ exclude_patterns: List of file patterns to exclude (e.g., ['*.css.ts', 'constants.ts'])
134
+ exclude_directories: List of directory names to exclude (e.g., ['models', 'types'])
135
+
136
+ Returns:
137
+ List of tuples: (file_path, coverage_metrics)
138
+ """
139
+ low_coverage_files = []
140
+
141
+ # Default exclude patterns if none provided
142
+ if exclude_patterns is None:
143
+ exclude_patterns = [
144
+ '*.css.ts', '*.css.tsx', '*.css', '*.less', '*.scss', '*.sass',
145
+ 'constants.ts', 'constants.js',
146
+ 'urls.ts', 'urls.js',
147
+ 'languageList.ts', 'languageList.js',
148
+ 'countryList.ts', 'countryList.js',
149
+ 'countryPhoneCodes.ts', 'countryPhoneCodes.js',
150
+ 'gdpr.ts', 'gdpr.js',
151
+ 'selectedColumns.ts', 'selectedColumns.js',
152
+ 'types.ts', 'types.js',
153
+ 'interfaces.ts', 'interfaces.js'
154
+ ]
155
+
156
+ if exclude_directories is None:
157
+ exclude_directories = ['models']
158
+
159
+ for file_path, metrics in coverage_data.items():
160
+ # Skip the 'total' summary entry
161
+ if file_path == 'total':
162
+ continue
163
+
164
+ # Check exclude patterns
165
+ should_exclude = False
166
+ for pattern in exclude_patterns:
167
+ if pattern.startswith('*.'):
168
+ # Extension pattern
169
+ ext = pattern[1:] # Remove the *
170
+ if file_path.endswith(ext):
171
+ should_exclude = True
172
+ break
173
+ else:
174
+ # Exact filename pattern
175
+ if file_path.endswith(f'/{pattern}') or file_path.endswith(f'\\{pattern}'):
176
+ should_exclude = True
177
+ break
178
+
179
+ if should_exclude:
180
+ continue
181
+
182
+ # Check exclude directories
183
+ for dir_name in exclude_directories:
184
+ if f'/{dir_name}/' in file_path or f'\\{dir_name}\\' in file_path:
185
+ should_exclude = True
186
+ break
187
+
188
+ if should_exclude:
189
+ continue
190
+
191
+ # Calculate average coverage
192
+ avg_coverage = calculate_average_coverage(metrics)
193
+
194
+ # Filter files below threshold
195
+ if avg_coverage < threshold:
196
+ # Make path relative to workspace if provided
197
+ display_path = file_path
198
+ if workspace_root:
199
+ try:
200
+ display_path = str(Path(file_path).relative_to(workspace_root))
201
+ except ValueError:
202
+ pass # Keep absolute path if not relative to workspace
203
+
204
+ low_coverage_files.append((display_path, metrics, avg_coverage))
205
+
206
+ # Sort by average coverage (lowest first)
207
+ low_coverage_files.sort(key=lambda x: x[2])
208
+
209
+ return low_coverage_files
210
+
211
+
212
+ def generate_markdown_report(
213
+ low_coverage_files: List[Tuple[str, Dict, float]],
214
+ threshold: float,
215
+ output_path: str,
216
+ top_n: int = 30,
217
+ show_full: bool = False
218
+ ) -> None:
219
+ """Generate a markdown report of files with low coverage."""
220
+
221
+ # Group by severity
222
+ zero_coverage = [f for f in low_coverage_files if f[2] == 0]
223
+ critical = [f for f in low_coverage_files if 0 < f[2] < 50]
224
+ moderate = [f for f in low_coverage_files if 50 <= f[2] < 70]
225
+ minor = [f for f in low_coverage_files if 70 <= f[2] < threshold]
226
+
227
+ with open(output_path, 'w') as f:
228
+ f.write("# Unit Test Coverage Report - Low Coverage Files\n\n")
229
+ f.write(f"Files with coverage below **{threshold}%** threshold.\n\n")
230
+
231
+ if not low_coverage_files:
232
+ f.write("āœ… All files meet the coverage threshold!\n")
233
+ return
234
+
235
+ # Summary statistics
236
+ f.write("## Summary Statistics\n\n")
237
+ f.write(f"**Total files needing attention:** {len(low_coverage_files)}\n\n")
238
+ f.write("| Severity | Count | Coverage Range |\n")
239
+ f.write("|----------|-------|----------------|\n")
240
+ f.write(f"| šŸ”“ Zero | {len(zero_coverage)} | 0% |\n")
241
+ f.write(f"| 🟠 Critical | {len(critical)} | <50% |\n")
242
+ f.write(f"| 🟔 Moderate | {len(moderate)} | 50-70% |\n")
243
+ f.write(f"| 🟢 Minor | {len(minor)} | 70-{threshold}% |\n\n")
244
+
245
+ # Top priority files
246
+ files_to_show = low_coverage_files if show_full else low_coverage_files[:top_n]
247
+
248
+ f.write(f"## Top {min(top_n, len(low_coverage_files))} Priority Files\n\n")
249
+ f.write("*Files with the lowest coverage - start here*\n\n")
250
+ f.write("| File | Avg Coverage | Lines | Statements | Functions | Branches |\n")
251
+ f.write("|------|--------------|-------|------------|-----------|----------|\n")
252
+
253
+ for file_path, metrics, avg_cov in files_to_show:
254
+ lines_pct = metrics.get('lines', {}).get('pct', 0)
255
+ stmts_pct = metrics.get('statements', {}).get('pct', 0)
256
+ funcs_pct = metrics.get('functions', {}).get('pct', 0)
257
+ branch_pct = metrics.get('branches', {}).get('pct', 0)
258
+
259
+ # Status icon
260
+ if avg_cov == 0:
261
+ status = "šŸ”“"
262
+ elif avg_cov < 50:
263
+ status = "🟠"
264
+ elif avg_cov < 70:
265
+ status = "🟔"
266
+ else:
267
+ status = "🟢"
268
+
269
+ f.write(f"| {status} `{file_path}` | **{avg_cov:.1f}%** | {lines_pct:.1f}% | {stmts_pct:.1f}% | {funcs_pct:.1f}% | {branch_pct:.1f}% |\n")
270
+
271
+ if not show_full and len(low_coverage_files) > top_n:
272
+ f.write(f"\n*...and {len(low_coverage_files) - top_n} more files*\n")
273
+
274
+ # Grouped breakdown by severity (condensed)
275
+ f.write("\n## Files by Severity\n\n")
276
+
277
+ if zero_coverage:
278
+ f.write(f"### šŸ”“ Zero Coverage ({len(zero_coverage)} files)\n\n")
279
+ f.write("**Priority: CRITICAL** - These files have no tests\n\n")
280
+ show_count = min(10, len(zero_coverage)) if not show_full else len(zero_coverage)
281
+ for i, (file_path, metrics, _) in enumerate(zero_coverage[:show_count]):
282
+ lines_total = metrics.get('lines', {}).get('total', 0)
283
+ funcs_total = metrics.get('functions', {}).get('total', 0)
284
+ f.write(f"{i+1}. `{file_path}` ({lines_total} lines, {funcs_total} functions)\n")
285
+ if not show_full and len(zero_coverage) > show_count:
286
+ f.write(f"\n*...and {len(zero_coverage) - show_count} more files*\n")
287
+ f.write("\n")
288
+
289
+ if critical:
290
+ f.write(f"### 🟠 Critical (<50% coverage, {len(critical)} files)\n\n")
291
+ show_count = min(10, len(critical)) if not show_full else len(critical)
292
+ for i, (file_path, metrics, avg_cov) in enumerate(critical[:show_count]):
293
+ f.write(f"{i+1}. `{file_path}` - **{avg_cov:.1f}%**\n")
294
+ if not show_full and len(critical) > show_count:
295
+ f.write(f"\n*...and {len(critical) - show_count} more files*\n")
296
+ f.write("\n")
297
+
298
+ if moderate:
299
+ f.write(f"### 🟔 Moderate (50-70% coverage, {len(moderate)} files)\n\n")
300
+ show_count = min(10, len(moderate)) if not show_full else len(moderate)
301
+ for i, (file_path, metrics, avg_cov) in enumerate(moderate[:show_count]):
302
+ f.write(f"{i+1}. `{file_path}` - **{avg_cov:.1f}%**\n")
303
+ if not show_full and len(moderate) > show_count:
304
+ f.write(f"\n*...and {len(moderate) - show_count} more files*\n")
305
+ f.write("\n")
306
+
307
+ if minor:
308
+ f.write(f"### 🟢 Minor (70-{threshold}% coverage, {len(minor)} files)\n\n")
309
+ show_count = min(10, len(minor)) if not show_full else len(minor)
310
+ for i, (file_path, metrics, avg_cov) in enumerate(minor[:show_count]):
311
+ f.write(f"{i+1}. `{file_path}` - **{avg_cov:.1f}%**\n")
312
+ if not show_full and len(minor) > show_count:
313
+ f.write(f"\n*...and {len(minor) - show_count} more files*\n")
314
+ f.write("\n")
315
+
316
+ # Next steps
317
+ f.write("## Recommended Action Plan\n\n")
318
+ f.write("1. **Start with šŸ”“ Zero Coverage files** - Write new tests from scratch\n")
319
+ f.write("2. **Address 🟠 Critical files** - Significantly expand test coverage\n")
320
+ f.write("3. **Improve 🟔 Moderate files** - Add missing test cases\n")
321
+ f.write("4. **Polish 🟢 Minor files** - Final push to achieve target coverage\n\n")
322
+
323
+ # Footer
324
+ f.write("---\n\n")
325
+ if not show_full and len(low_coverage_files) > top_n:
326
+ f.write("šŸ’” **Tip:** Run with `--full` flag to see all files\n\n")
327
+ f.write("*Report generated from Jest coverage-summary.json*\n")
328
+
329
+
330
+ def main():
331
+ """Main entry point."""
332
+ import argparse
333
+
334
+ parser = argparse.ArgumentParser(
335
+ description='Filter files with low test coverage from Jest coverage report'
336
+ )
337
+ parser.add_argument(
338
+ 'coverage_report',
339
+ nargs='?',
340
+ help='Path to coverage-summary.json file (auto-detected from config if omitted)'
341
+ )
342
+ parser.add_argument(
343
+ '-o', '--output',
344
+ help='Output markdown file path (default from config or low-coverage-report.md)'
345
+ )
346
+ parser.add_argument(
347
+ '-t', '--threshold',
348
+ type=float,
349
+ help='Coverage threshold percentage (default from config or 90.0)'
350
+ )
351
+ parser.add_argument(
352
+ '-w', '--workspace',
353
+ help='Workspace root path for relative file paths (auto-detected if omitted)'
354
+ )
355
+ parser.add_argument(
356
+ '--config',
357
+ help='Path to project.config.json file (default: .documents-design/project.config.json)'
358
+ )
359
+ parser.add_argument(
360
+ '-n', '--top-n',
361
+ type=int,
362
+ help='Number of top priority files to show (default from config or 30)'
363
+ )
364
+ parser.add_argument(
365
+ '--full',
366
+ action='store_true',
367
+ help='Show all files instead of just top N (default from config or False)'
368
+ )
369
+
370
+ args = parser.parse_args()
371
+
372
+ # Try to load configuration
373
+ workspace_root = args.workspace or os.getcwd()
374
+
375
+ # Detect config file location
376
+ config_path = None
377
+ if workspace_root:
378
+ search_paths = [
379
+ Path(workspace_root) / '.documents-design' / 'project.config.json',
380
+ ]
381
+ for path in search_paths:
382
+ if path.exists():
383
+ config_path = path
384
+ break
385
+ else:
386
+ config_path = find_config_file()
387
+
388
+ config = load_project_config(workspace_root)
389
+
390
+ if config and config.get('enabled', True):
391
+ print(f"šŸ“‹ Loaded configuration from {config_path}")
392
+
393
+ # Use config values as defaults, CLI args override
394
+ coverage_report = args.coverage_report or config.get('coverageReportPath')
395
+ output_path = args.output or config.get('outputReportPath', 'low-coverage-report.md')
396
+ threshold = args.threshold if args.threshold is not None else config.get('threshold', 90.0)
397
+ top_n = args.top_n if args.top_n is not None else config.get('reportTopN', 30)
398
+ show_full = args.full or config.get('reportShowFullList', False)
399
+ exclude_patterns = config.get('excludePatterns', None)
400
+ exclude_directories = config.get('excludeDirectories', None)
401
+ else:
402
+ # No config or config disabled, use CLI args or defaults
403
+ if not args.coverage_report:
404
+ print("Error: coverage_report argument required when no config is available", file=sys.stderr)
405
+ print("", file=sys.stderr)
406
+ print("Create configuration at:", file=sys.stderr)
407
+ print(" - .documents-design/project.config.json", file=sys.stderr)
408
+ print("", file=sys.stderr)
409
+ print("Or provide coverage report path explicitly", file=sys.stderr)
410
+ sys.exit(1)
411
+
412
+ coverage_report = args.coverage_report
413
+ output_path = args.output or 'low-coverage-report.md'
414
+ threshold = args.threshold if args.threshold is not None else 90.0
415
+ top_n = args.top_n if args.top_n is not None else 30
416
+ show_full = args.full
417
+ exclude_patterns = None
418
+ exclude_directories = None
419
+
420
+ print(f"šŸ“Š Loading coverage report: {coverage_report}")
421
+ coverage_data = load_coverage_report(coverage_report)
422
+
423
+ print(f"šŸ” Filtering files with coverage < {threshold}%")
424
+ low_coverage_files = filter_low_coverage_files(
425
+ coverage_data,
426
+ threshold,
427
+ workspace_root,
428
+ exclude_patterns,
429
+ exclude_directories
430
+ )
431
+
432
+ report_mode = "all files" if show_full else f"top {top_n} files"
433
+ print(f"šŸ“ Generating markdown report ({report_mode}): {output_path}")
434
+ generate_markdown_report(low_coverage_files, threshold, output_path, top_n, show_full)
435
+
436
+ print(f"\nāœ… Report generated successfully!")
437
+ print(f" Found {len(low_coverage_files)} files below {threshold}% coverage")
438
+ print(f" Report saved to: {output_path}")
439
+
440
+ if config:
441
+ excluded_count = len(exclude_patterns or [])
442
+ excluded_dirs = len(exclude_directories or [])
443
+ print(f" Excluded patterns: {excluded_count} file patterns, {excluded_dirs} directories")
444
+
445
+
446
+ if __name__ == '__main__':
447
+ main()