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.
- package/README.md +506 -0
- package/kits/agent/.agent/skills/claude-code-subagent-creator/SKILL.md +292 -0
- package/kits/agent/.agent/skills/claude-code-subagent-creator/references/claude-code-subagent-configuration.md +158 -0
- package/kits/agent/.agent/skills/claude-code-subagent-creator/templates/subagent-profile.template.md +26 -0
- package/kits/agent/.agent/skills/skill-creator/LICENSE.txt +202 -0
- package/kits/agent/.agent/skills/skill-creator/SKILL.md +485 -0
- package/kits/agent/.agent/skills/skill-creator/agents/analyzer.md +274 -0
- package/kits/agent/.agent/skills/skill-creator/agents/comparator.md +202 -0
- package/kits/agent/.agent/skills/skill-creator/agents/grader.md +223 -0
- package/kits/agent/.agent/skills/skill-creator/assets/eval_review.html +146 -0
- package/kits/agent/.agent/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/kits/agent/.agent/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/kits/agent/.agent/skills/skill-creator/references/schemas.md +430 -0
- package/kits/agent/.agent/skills/skill-creator/scripts/__init__.py +0 -0
- package/kits/agent/.agent/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/kits/agent/.agent/skills/skill-creator/scripts/generate_report.py +326 -0
- package/kits/agent/.agent/skills/skill-creator/scripts/improve_description.py +247 -0
- package/kits/agent/.agent/skills/skill-creator/scripts/package_skill.py +136 -0
- package/kits/agent/.agent/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/kits/agent/.agent/skills/skill-creator/scripts/run_eval.py +310 -0
- package/kits/agent/.agent/skills/skill-creator/scripts/run_loop.py +328 -0
- package/kits/agent/.agent/skills/skill-creator/scripts/utils.py +47 -0
- package/kits/agent/manifest.json +10 -0
- package/kits/claude/.claude/agents/code-pusher.md +46 -0
- package/kits/claude/.claude/agents/feature-document-updater.md +37 -0
- package/kits/claude/.claude/agents/self-reviewer.md +32 -0
- package/kits/claude/.claude/agents/web-auto-agentic-workflow-initializer.md +42 -0
- package/kits/claude/.claude/agents/web-auto-assisted-fix-and-runner.md +36 -0
- package/kits/claude/.claude/agents/web-auto-chrome-devtools-selector-extractor.md +36 -0
- package/kits/claude/.claude/agents/web-auto-coder.md +33 -0
- package/kits/claude/.claude/agents/web-auto-fe-selector-extractor.md +31 -0
- package/kits/claude/.claude/agents/web-auto-fix-and-runner.md +35 -0
- package/kits/claude/.claude/agents/web-auto-lessons-learned-extractor.md +34 -0
- package/kits/claude/.claude/agents/web-auto-playwright-mcp-selector-extractor.md +37 -0
- package/kits/claude/.claude/agents/web-auto-source-instructions-updater.md +43 -0
- package/kits/claude/.claude/agents/web-auto-test-cases-generator.md +29 -0
- package/kits/claude/.claude/agents/web-auto-ticket-designer.md +35 -0
- package/kits/claude/.claude/agents/web-auto-ticket-playbook-planner.md +36 -0
- package/kits/claude/.claude/agents/web-auto.md +382 -0
- package/kits/claude/.claude/skills/claude-code-subagent-creator/SKILL.md +292 -0
- package/kits/claude/.claude/skills/claude-code-subagent-creator/references/claude-code-subagent-configuration.md +158 -0
- package/kits/claude/.claude/skills/claude-code-subagent-creator/templates/subagent-profile.template.md +26 -0
- package/kits/claude/.claude/skills/skill-creator/LICENSE.txt +202 -0
- package/kits/claude/.claude/skills/skill-creator/SKILL.md +485 -0
- package/kits/claude/.claude/skills/skill-creator/agents/analyzer.md +274 -0
- package/kits/claude/.claude/skills/skill-creator/agents/comparator.md +202 -0
- package/kits/claude/.claude/skills/skill-creator/agents/grader.md +223 -0
- package/kits/claude/.claude/skills/skill-creator/assets/eval_review.html +146 -0
- package/kits/claude/.claude/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/kits/claude/.claude/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/kits/claude/.claude/skills/skill-creator/references/schemas.md +430 -0
- package/kits/claude/.claude/skills/skill-creator/scripts/__init__.py +0 -0
- package/kits/claude/.claude/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/kits/claude/.claude/skills/skill-creator/scripts/generate_report.py +326 -0
- package/kits/claude/.claude/skills/skill-creator/scripts/improve_description.py +247 -0
- package/kits/claude/.claude/skills/skill-creator/scripts/package_skill.py +136 -0
- package/kits/claude/.claude/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/kits/claude/.claude/skills/skill-creator/scripts/run_eval.py +310 -0
- package/kits/claude/.claude/skills/skill-creator/scripts/run_loop.py +328 -0
- package/kits/claude/.claude/skills/skill-creator/scripts/utils.py +47 -0
- package/kits/claude/manifest.json +10 -0
- package/kits/cursor/.cursor/agents/code-pusher.agent.md +43 -0
- package/kits/cursor/.cursor/agents/feature-document-updater.agent.md +34 -0
- package/kits/cursor/.cursor/agents/self-reviewer.agent.md +29 -0
- package/kits/cursor/.cursor/agents/web-auto-agentic-workflow-initializer.agent.md +37 -0
- package/kits/cursor/.cursor/agents/web-auto-assisted-fix-and-runner.agent.md +33 -0
- package/kits/cursor/.cursor/agents/web-auto-chrome-devtools-selector-extractor.agent.md +31 -0
- package/kits/cursor/.cursor/agents/web-auto-coder.agent.md +30 -0
- package/kits/cursor/.cursor/agents/web-auto-fe-selector-extractor.agent.md +28 -0
- package/kits/cursor/.cursor/agents/web-auto-fix-and-runner.agent.md +32 -0
- package/kits/cursor/.cursor/agents/web-auto-lessons-learned-extractor.agent.md +31 -0
- package/kits/cursor/.cursor/agents/web-auto-playwright-mcp-selector-extractor.agent.md +32 -0
- package/kits/cursor/.cursor/agents/web-auto-source-instructions-updater.agent.md +40 -0
- package/kits/cursor/.cursor/agents/web-auto-test-cases-generator.agent.md +26 -0
- package/kits/cursor/.cursor/agents/web-auto-ticket-designer.agent.md +32 -0
- package/kits/cursor/.cursor/agents/web-auto-ticket-playbook-planner.agent.md +33 -0
- package/kits/cursor/.cursor/agents/web-auto.agent.md +379 -0
- package/kits/cursor/.cursor/skills/claude-code-subagent-creator/SKILL.md +292 -0
- package/kits/cursor/.cursor/skills/claude-code-subagent-creator/references/claude-code-subagent-configuration.md +158 -0
- package/kits/cursor/.cursor/skills/claude-code-subagent-creator/templates/subagent-profile.template.md +26 -0
- package/kits/cursor/.cursor/skills/skill-creator/LICENSE.txt +202 -0
- package/kits/cursor/.cursor/skills/skill-creator/SKILL.md +485 -0
- package/kits/cursor/.cursor/skills/skill-creator/agents/analyzer.md +274 -0
- package/kits/cursor/.cursor/skills/skill-creator/agents/comparator.md +202 -0
- package/kits/cursor/.cursor/skills/skill-creator/agents/grader.md +223 -0
- package/kits/cursor/.cursor/skills/skill-creator/assets/eval_review.html +146 -0
- package/kits/cursor/.cursor/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/kits/cursor/.cursor/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/kits/cursor/.cursor/skills/skill-creator/references/schemas.md +430 -0
- package/kits/cursor/.cursor/skills/skill-creator/scripts/__init__.py +0 -0
- package/kits/cursor/.cursor/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/kits/cursor/.cursor/skills/skill-creator/scripts/generate_report.py +326 -0
- package/kits/cursor/.cursor/skills/skill-creator/scripts/improve_description.py +247 -0
- package/kits/cursor/.cursor/skills/skill-creator/scripts/package_skill.py +136 -0
- package/kits/cursor/.cursor/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/kits/cursor/.cursor/skills/skill-creator/scripts/run_eval.py +310 -0
- package/kits/cursor/.cursor/skills/skill-creator/scripts/run_loop.py +328 -0
- package/kits/cursor/.cursor/skills/skill-creator/scripts/utils.py +47 -0
- package/kits/cursor/manifest.json +10 -0
- package/kits/github/.github/agents/code-pusher.agent.md +45 -0
- package/kits/github/.github/agents/feature-document-updater.agent.md +36 -0
- package/kits/github/.github/agents/self-reviewer.agent.md +31 -0
- package/kits/github/.github/agents/web-auto-agentic-workflow-initializer.agent.md +39 -0
- package/kits/github/.github/agents/web-auto-assisted-fix-and-runner.agent.md +35 -0
- package/kits/github/.github/agents/web-auto-chrome-devtools-selector-extractor.agent.md +33 -0
- package/kits/github/.github/agents/web-auto-coder.agent.md +32 -0
- package/kits/github/.github/agents/web-auto-fe-selector-extractor.agent.md +30 -0
- package/kits/github/.github/agents/web-auto-fix-and-runner.agent.md +34 -0
- package/kits/github/.github/agents/web-auto-lessons-learned-extractor.agent.md +33 -0
- package/kits/github/.github/agents/web-auto-playwright-mcp-selector-extractor.agent.md +34 -0
- package/kits/github/.github/agents/web-auto-source-instructions-updater.agent.md +42 -0
- package/kits/github/.github/agents/web-auto-test-cases-generator.agent.md +28 -0
- package/kits/github/.github/agents/web-auto-ticket-designer.agent.md +34 -0
- package/kits/github/.github/agents/web-auto-ticket-playbook-creator.agent.md +35 -0
- package/kits/github/.github/agents/web-auto.agent.md +382 -0
- package/kits/github/.github/skills/claude-code-subagent-creator/SKILL.md +310 -0
- package/kits/github/.github/skills/claude-code-subagent-creator/references/claude-code-subagent-configuration.md +158 -0
- package/kits/github/.github/skills/claude-code-subagent-creator/templates/subagent-profile.template.md +37 -0
- package/kits/github/.github/skills/skill-creator/LICENSE.txt +202 -0
- package/kits/github/.github/skills/skill-creator/SKILL.md +485 -0
- package/kits/github/.github/skills/skill-creator/agents/analyzer.md +274 -0
- package/kits/github/.github/skills/skill-creator/agents/comparator.md +202 -0
- package/kits/github/.github/skills/skill-creator/agents/grader.md +223 -0
- package/kits/github/.github/skills/skill-creator/assets/eval_review.html +146 -0
- package/kits/github/.github/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/kits/github/.github/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/kits/github/.github/skills/skill-creator/references/schemas.md +430 -0
- package/kits/github/.github/skills/skill-creator/scripts/__init__.py +0 -0
- package/kits/github/.github/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/kits/github/.github/skills/skill-creator/scripts/generate_report.py +326 -0
- package/kits/github/.github/skills/skill-creator/scripts/improve_description.py +247 -0
- package/kits/github/.github/skills/skill-creator/scripts/package_skill.py +136 -0
- package/kits/github/.github/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/kits/github/.github/skills/skill-creator/scripts/run_eval.py +310 -0
- package/kits/github/.github/skills/skill-creator/scripts/run_loop.py +328 -0
- package/kits/github/.github/skills/skill-creator/scripts/utils.py +47 -0
- package/kits/github/manifest.json +10 -0
- package/kits/shared/docs/ai-code-review.md +440 -0
- package/kits/shared/docs/increase-unit-test-coverage.md +77 -0
- package/kits/shared/docs/pr-review-agent.md +501 -0
- package/kits/shared/docs/self-review-agent.md +246 -0
- package/kits/shared/docs/web-auto-agentic-workflow.md +506 -0
- package/kits/shared/manifest.json +11 -0
- package/kits/shared/skills/fix-automation-tests/SKILL.md +280 -0
- package/kits/shared/skills/fix-automation-tests/scripts/fetch_pr_changes.py +300 -0
- package/kits/shared/skills/fix-automation-tests/templates/impact-report.template.md +42 -0
- package/kits/shared/skills/increase-unit-test-coverage/SKILL.md +117 -0
- package/kits/shared/skills/increase-unit-test-coverage/scripts/filter_low_coverage.py +447 -0
- package/kits/shared/skills/pr-review/SKILL.md +200 -0
- package/kits/shared/skills/pr-review/references/automation.md +62 -0
- package/kits/shared/skills/pr-review/references/backend.md +95 -0
- package/kits/shared/skills/pr-review/references/frontend.md +103 -0
- package/kits/shared/skills/pr-review/references/mobile.md +108 -0
- package/kits/shared/skills/pr-review/references/output-schema.md +130 -0
- package/kits/shared/skills/pr-review/scripts/post-review.py +1395 -0
- package/kits/shared/skills/push-code/SKILL.md +176 -0
- package/kits/shared/skills/self-review/SKILL.md +234 -0
- package/kits/shared/skills/self-review/evals/evals.json +23 -0
- package/kits/shared/skills/self-review/references/automation.md +62 -0
- package/kits/shared/skills/self-review/references/backend.md +95 -0
- package/kits/shared/skills/self-review/references/frontend.md +103 -0
- package/kits/shared/skills/self-review/references/mobile.md +108 -0
- package/kits/shared/skills/self-review/templates/issues.template.md +72 -0
- package/kits/shared/skills/update-feature-document/SKILL.md +156 -0
- package/kits/shared/skills/update-feature-document/templates/delta.template.yaml +58 -0
- package/kits/shared/skills/update-feature-document/templates/feature.template.md +25 -0
- package/kits/shared/skills/web-auto-assisted-fix-and-run/SKILL.md +130 -0
- package/kits/shared/skills/web-auto-assisted-fix-and-run/references/resolve-api-error.md +108 -0
- package/kits/shared/skills/web-auto-assisted-fix-and-run/references/resolve-selector.md +60 -0
- package/kits/shared/skills/web-auto-assisted-fix-and-run/templates/issues-resolution-report-append.template.md +54 -0
- package/kits/shared/skills/web-auto-chrome-devtools-mcp-extract-selectors/SKILL.md +284 -0
- package/kits/shared/skills/web-auto-coding/SKILL.md +152 -0
- package/kits/shared/skills/web-auto-extract-lessons-learned/SKILL.md +168 -0
- package/kits/shared/skills/web-auto-extract-lessons-learned/templates/lessons-learned.template.md +115 -0
- package/kits/shared/skills/web-auto-fe-extract-selectors/SKILL.md +282 -0
- package/kits/shared/skills/web-auto-fe-extract-selectors/evals/evals.json +23 -0
- package/kits/shared/skills/web-auto-fix-and-run-test/SKILL.md +183 -0
- package/kits/shared/skills/web-auto-fix-and-run-test/templates/issues-resolution-report.template.md +77 -0
- package/kits/shared/skills/web-auto-generate-best-practices/SKILL.md +123 -0
- package/kits/shared/skills/web-auto-generate-instructions/SKILL.md +200 -0
- package/kits/shared/skills/web-auto-generate-instructions/evals/evals.json +23 -0
- package/kits/shared/skills/web-auto-generate-instructions/references/analysis-guide.md +145 -0
- package/kits/shared/skills/web-auto-generate-instructions/templates/web-auto-instructions.template.md +184 -0
- package/kits/shared/skills/web-auto-generate-project-blueprint/SKILL.md +181 -0
- package/kits/shared/skills/web-auto-generate-project-blueprint/evals/evals.json +57 -0
- package/kits/shared/skills/web-auto-generate-project-blueprint/templates/web-auto-project-blueprint.template.md +161 -0
- package/kits/shared/skills/web-auto-playwright-mcp-extract-selectors/SKILL.md +293 -0
- package/kits/shared/skills/web-auto-test-cases/SKILL.md +138 -0
- package/kits/shared/skills/web-auto-test-cases/evals/evals.json +129 -0
- package/kits/shared/skills/web-auto-test-cases/templates/test-cases.template.md +53 -0
- package/kits/shared/skills/web-auto-ticket-design/SKILL.md +199 -0
- package/kits/shared/skills/web-auto-ticket-design/templates/ticket-design.template.md +138 -0
- package/kits/shared/skills/web-auto-ticket-playbook/SKILL.md +218 -0
- package/kits/shared/skills/web-auto-ticket-playbook/evals/evals.json +23 -0
- package/kits/shared/skills/web-auto-ticket-playbook/templates/ticket-playbook.template.md +148 -0
- package/kits/shared/skills/web-auto-update-source-instructions/SKILL.md +156 -0
- package/kits/shared/skills/web-auto-update-source-instructions/evals/evals.json +22 -0
- package/kits/shared/skills/workspace-ai-nav-creator/SKILL.md +168 -0
- package/kits/shared/skills/workspace-ai-nav-creator/templates/agents-md.template.md +112 -0
- package/kits/shared/skills/workspace-ai-nav-creator/templates/claude-md.template.md +86 -0
- 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()
|