@redaksjon/brennpunkt 0.0.1
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/LICENSE +190 -0
- package/README.md +619 -0
- package/dist/analyzer.js +115 -0
- package/dist/analyzer.js.map +1 -0
- package/dist/main.js +376 -0
- package/dist/main.js.map +1 -0
- package/dist/mcp/server.js +461 -0
- package/dist/mcp/server.js.map +1 -0
- package/guide/ai-integration.md +404 -0
- package/guide/api.md +324 -0
- package/guide/configuration.md +235 -0
- package/guide/index.md +177 -0
- package/guide/integration.md +283 -0
- package/guide/quickstart.md +119 -0
- package/guide/scoring.md +172 -0
- package/guide/usage.md +249 -0
- package/package.json +77 -0
package/guide/scoring.md
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# Priority Scoring
|
|
2
|
+
|
|
3
|
+
How brennpunkt calculates priority scores to identify where testing efforts will have the biggest impact.
|
|
4
|
+
|
|
5
|
+
## The Formula
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
priorityScore = (branchGap × branchWeight + functionGap × functionWeight + lineGap × lineWeight) × log₁₀(lines + 1)
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Where:
|
|
12
|
+
- `branchGap` = 100 - branch coverage percentage
|
|
13
|
+
- `functionGap` = 100 - function coverage percentage
|
|
14
|
+
- `lineGap` = 100 - line coverage percentage
|
|
15
|
+
- `lines` = total lines of code in the file
|
|
16
|
+
|
|
17
|
+
## Default Weights
|
|
18
|
+
|
|
19
|
+
| Metric | Weight | Reason |
|
|
20
|
+
|--------|--------|--------|
|
|
21
|
+
| Branch Coverage | 0.5 (50%) | Untested branches hide conditional bugs |
|
|
22
|
+
| Function Coverage | 0.3 (30%) | Untested functions indicate dead code or missing tests |
|
|
23
|
+
| Line Coverage | 0.2 (20%) | Basic execution path; high coverage doesn't guarantee correctness |
|
|
24
|
+
|
|
25
|
+
## Why These Weights?
|
|
26
|
+
|
|
27
|
+
### Branch Coverage (50%)
|
|
28
|
+
|
|
29
|
+
Branches are the most important because:
|
|
30
|
+
- A function can execute but still have untested error paths
|
|
31
|
+
- Untested `if/else` and `switch` cases often hide bugs
|
|
32
|
+
- Edge cases and error handling live in branches
|
|
33
|
+
|
|
34
|
+
**Example**: A login function might have 100% line coverage but 50% branch coverage because the "invalid password" path was never tested.
|
|
35
|
+
|
|
36
|
+
### Function Coverage (30%)
|
|
37
|
+
|
|
38
|
+
Untested functions indicate:
|
|
39
|
+
- Dead code that should be removed
|
|
40
|
+
- Missing feature tests
|
|
41
|
+
- Code paths that aren't exercised
|
|
42
|
+
|
|
43
|
+
### Line Coverage (20%)
|
|
44
|
+
|
|
45
|
+
Line coverage is the baseline but:
|
|
46
|
+
- High line coverage doesn't mean the code is correct
|
|
47
|
+
- It only shows that lines executed, not that they work correctly
|
|
48
|
+
- Often inflated by simple getter/setter tests
|
|
49
|
+
|
|
50
|
+
## Size Scaling
|
|
51
|
+
|
|
52
|
+
The `log₁₀(lines + 1)` factor means:
|
|
53
|
+
- Larger files with low coverage rank higher
|
|
54
|
+
- A 1000-line file at 50% coverage ranks higher than a 10-line file at 50%
|
|
55
|
+
- The logarithmic scale prevents huge files from dominating
|
|
56
|
+
|
|
57
|
+
**Example scaling**:
|
|
58
|
+
| Lines | Multiplier |
|
|
59
|
+
|-------|------------|
|
|
60
|
+
| 10 | 1.04 |
|
|
61
|
+
| 100 | 2.00 |
|
|
62
|
+
| 500 | 2.70 |
|
|
63
|
+
| 1000 | 3.00 |
|
|
64
|
+
| 5000 | 3.70 |
|
|
65
|
+
|
|
66
|
+
## Example Calculations
|
|
67
|
+
|
|
68
|
+
### File A: Large file, low coverage
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
file: src/complex-module.ts
|
|
72
|
+
lines: 500
|
|
73
|
+
branchCoverage: 40%
|
|
74
|
+
functionCoverage: 60%
|
|
75
|
+
lineCoverage: 70%
|
|
76
|
+
|
|
77
|
+
branchGap = 100 - 40 = 60
|
|
78
|
+
functionGap = 100 - 60 = 40
|
|
79
|
+
lineGap = 100 - 70 = 30
|
|
80
|
+
|
|
81
|
+
weightedGap = (60 × 0.5) + (40 × 0.3) + (30 × 0.2)
|
|
82
|
+
= 30 + 12 + 6 = 48
|
|
83
|
+
|
|
84
|
+
priorityScore = 48 × log₁₀(501)
|
|
85
|
+
= 48 × 2.70
|
|
86
|
+
= 129.6
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### File B: Small file, very low coverage
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
file: src/tiny-util.ts
|
|
93
|
+
lines: 20
|
|
94
|
+
branchCoverage: 0%
|
|
95
|
+
functionCoverage: 0%
|
|
96
|
+
lineCoverage: 50%
|
|
97
|
+
|
|
98
|
+
branchGap = 100
|
|
99
|
+
functionGap = 100
|
|
100
|
+
lineGap = 50
|
|
101
|
+
|
|
102
|
+
weightedGap = (100 × 0.5) + (100 × 0.3) + (50 × 0.2)
|
|
103
|
+
= 50 + 30 + 10 = 90
|
|
104
|
+
|
|
105
|
+
priorityScore = 90 × log₁₀(21)
|
|
106
|
+
= 90 × 1.32
|
|
107
|
+
= 118.8
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Even though File B has worse coverage percentages, File A ranks higher because it's a larger file with more impact.
|
|
111
|
+
|
|
112
|
+
## Customizing Weights
|
|
113
|
+
|
|
114
|
+
### Prioritize Branch Coverage
|
|
115
|
+
|
|
116
|
+
When you want to focus on conditional logic:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
brennpunkt --weights 0.7,0.2,0.1
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Good for:
|
|
123
|
+
- Complex business logic
|
|
124
|
+
- Error handling code
|
|
125
|
+
- Authentication/authorization
|
|
126
|
+
|
|
127
|
+
### Equal Weights
|
|
128
|
+
|
|
129
|
+
When all coverage types matter equally:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
brennpunkt --weights 0.33,0.33,0.34
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Good for:
|
|
136
|
+
- General code quality
|
|
137
|
+
- Balanced improvement
|
|
138
|
+
|
|
139
|
+
### Prioritize Function Coverage
|
|
140
|
+
|
|
141
|
+
When you suspect dead code:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
brennpunkt --weights 0.2,0.6,0.2
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Good for:
|
|
148
|
+
- Legacy codebases
|
|
149
|
+
- Code cleanup projects
|
|
150
|
+
- Finding unused features
|
|
151
|
+
|
|
152
|
+
## Interpreting Scores
|
|
153
|
+
|
|
154
|
+
| Score Range | Priority | Action |
|
|
155
|
+
|-------------|----------|--------|
|
|
156
|
+
| > 100 | Critical | Focus testing efforts here immediately |
|
|
157
|
+
| 50-100 | High | Should be addressed in next sprint |
|
|
158
|
+
| 20-50 | Medium | Add to tech debt backlog |
|
|
159
|
+
| < 20 | Low | Acceptable for now |
|
|
160
|
+
|
|
161
|
+
## Score = 0
|
|
162
|
+
|
|
163
|
+
A score of 0 means:
|
|
164
|
+
- 100% coverage on all metrics, OR
|
|
165
|
+
- File excluded by `--min-lines` filter
|
|
166
|
+
|
|
167
|
+
## Files Not Scored
|
|
168
|
+
|
|
169
|
+
Files are excluded from scoring if:
|
|
170
|
+
- They have fewer lines than `--min-lines` threshold
|
|
171
|
+
- They have no coverage data (not instrumented)
|
|
172
|
+
- They're test files or config files (depending on your coverage setup)
|
package/guide/usage.md
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# Usage Reference
|
|
2
|
+
|
|
3
|
+
Complete CLI reference for brennpunkt.
|
|
4
|
+
|
|
5
|
+
## Basic Syntax
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
brennpunkt [coverage-path] [options]
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Arguments
|
|
12
|
+
|
|
13
|
+
### `[coverage-path]`
|
|
14
|
+
|
|
15
|
+
Path to the lcov.info file. Optional - if not provided, brennpunkt auto-discovers the file.
|
|
16
|
+
|
|
17
|
+
**Auto-discovery locations** (searched in order):
|
|
18
|
+
|
|
19
|
+
1. `coverage/lcov.info` - Jest, Vitest, c8
|
|
20
|
+
2. `.coverage/lcov.info` - Some configurations
|
|
21
|
+
3. `coverage/lcov/lcov.info` - Karma
|
|
22
|
+
4. `lcov.info` - Project root
|
|
23
|
+
5. `.nyc_output/lcov.info` - NYC legacy
|
|
24
|
+
6. `test-results/lcov.info` - Some CI configs
|
|
25
|
+
|
|
26
|
+
## Options
|
|
27
|
+
|
|
28
|
+
### `-w, --weights <weights>`
|
|
29
|
+
|
|
30
|
+
Custom weights for branches, functions, and lines coverage. Must be three comma-separated numbers.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Default: branches=0.5, functions=0.3, lines=0.2
|
|
34
|
+
brennpunkt --weights 0.5,0.3,0.2
|
|
35
|
+
|
|
36
|
+
# Prioritize branch coverage heavily
|
|
37
|
+
brennpunkt --weights 0.7,0.2,0.1
|
|
38
|
+
|
|
39
|
+
# Equal weights
|
|
40
|
+
brennpunkt --weights 0.33,0.33,0.34
|
|
41
|
+
|
|
42
|
+
# Focus on function coverage
|
|
43
|
+
brennpunkt --weights 0.2,0.6,0.2
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### `-m, --min-lines <number>`
|
|
47
|
+
|
|
48
|
+
Exclude files with fewer than N lines. Helps filter out tiny utility files.
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Default: 10 lines
|
|
52
|
+
brennpunkt --min-lines 10
|
|
53
|
+
|
|
54
|
+
# Only analyze files with 50+ lines
|
|
55
|
+
brennpunkt --min-lines 50
|
|
56
|
+
|
|
57
|
+
# Include all files (even 1-line files)
|
|
58
|
+
brennpunkt --min-lines 0
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### `-t, --top <number>`
|
|
62
|
+
|
|
63
|
+
Show only the top N priority files.
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Show top 10
|
|
67
|
+
brennpunkt --top 10
|
|
68
|
+
|
|
69
|
+
# Show top 3
|
|
70
|
+
brennpunkt --top 3
|
|
71
|
+
|
|
72
|
+
# Show all files (default)
|
|
73
|
+
brennpunkt
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### `-j, --json`
|
|
77
|
+
|
|
78
|
+
Output results as JSON instead of a formatted table.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# JSON output
|
|
82
|
+
brennpunkt --json
|
|
83
|
+
|
|
84
|
+
# JSON with top 5
|
|
85
|
+
brennpunkt --json --top 5
|
|
86
|
+
|
|
87
|
+
# Save to file
|
|
88
|
+
brennpunkt --json > coverage-priority.json
|
|
89
|
+
|
|
90
|
+
# Pipe to jq
|
|
91
|
+
brennpunkt --json | jq '.files[0].file'
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### `-c, --config <path>`
|
|
95
|
+
|
|
96
|
+
Specify a custom configuration file path.
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Use specific config
|
|
100
|
+
brennpunkt --config .config/brennpunkt.yaml
|
|
101
|
+
|
|
102
|
+
# Use config in different location
|
|
103
|
+
brennpunkt --config ~/brennpunkt-global.yaml
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### `--init-config`
|
|
107
|
+
|
|
108
|
+
Generate a default `brennpunkt.yaml` configuration file.
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# Create in current directory
|
|
112
|
+
brennpunkt --init-config
|
|
113
|
+
|
|
114
|
+
# Create at specific path
|
|
115
|
+
brennpunkt --init-config --config .config/brennpunkt.yaml
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### `--check-config`
|
|
119
|
+
|
|
120
|
+
Display the resolved configuration with source tracking.
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
brennpunkt --check-config
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Output:
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
================================================================================
|
|
130
|
+
BRENNPUNKT CONFIGURATION
|
|
131
|
+
================================================================================
|
|
132
|
+
|
|
133
|
+
Config file: brennpunkt.yaml
|
|
134
|
+
Status: Found
|
|
135
|
+
|
|
136
|
+
RESOLVED CONFIGURATION:
|
|
137
|
+
--------------------------------------------------------------------------------
|
|
138
|
+
[config file] coveragePath : "coverage/lcov.info"
|
|
139
|
+
[config file] weights : "0.6,0.2,0.2"
|
|
140
|
+
[default] minLines : 10
|
|
141
|
+
[default] json : false
|
|
142
|
+
[config file] top : 20
|
|
143
|
+
|
|
144
|
+
================================================================================
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### `-V, --version`
|
|
148
|
+
|
|
149
|
+
Show version number.
|
|
150
|
+
|
|
151
|
+
### `-h, --help`
|
|
152
|
+
|
|
153
|
+
Show help message.
|
|
154
|
+
|
|
155
|
+
## Examples
|
|
156
|
+
|
|
157
|
+
### Basic Usage
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
# Analyze current project
|
|
161
|
+
brennpunkt
|
|
162
|
+
|
|
163
|
+
# Analyze specific file
|
|
164
|
+
brennpunkt path/to/lcov.info
|
|
165
|
+
|
|
166
|
+
# Show top priorities
|
|
167
|
+
brennpunkt --top 10
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### CI/CD Usage
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
# GitHub Actions / CI pipeline
|
|
174
|
+
npx @redaksjon/brennpunkt --top 10
|
|
175
|
+
|
|
176
|
+
# Save JSON artifact
|
|
177
|
+
npx @redaksjon/brennpunkt --json > coverage-priority.json
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Automation
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
# Get top priority file
|
|
184
|
+
TOP_FILE=$(brennpunkt --json --top 1 | jq -r '.files[0].file')
|
|
185
|
+
echo "Focus on: $TOP_FILE"
|
|
186
|
+
|
|
187
|
+
# Check if any file has score > 100
|
|
188
|
+
brennpunkt --json | jq '.files[] | select(.priorityScore > 100)'
|
|
189
|
+
|
|
190
|
+
# Fail if highest priority score is too high
|
|
191
|
+
SCORE=$(brennpunkt --json --top 1 | jq '.files[0].priorityScore')
|
|
192
|
+
if (( $(echo "$SCORE > 150" | bc -l) )); then
|
|
193
|
+
echo "Critical coverage gap detected!"
|
|
194
|
+
exit 1
|
|
195
|
+
fi
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Combining Options
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
# Full analysis with custom weights, filtered
|
|
202
|
+
brennpunkt --weights 0.6,0.2,0.2 --min-lines 50 --top 20
|
|
203
|
+
|
|
204
|
+
# JSON output for top priorities
|
|
205
|
+
brennpunkt --json --top 5 --min-lines 20
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Exit Codes
|
|
209
|
+
|
|
210
|
+
| Code | Meaning |
|
|
211
|
+
|------|---------|
|
|
212
|
+
| 0 | Success |
|
|
213
|
+
| 1 | Error (file not found, invalid arguments, etc.) |
|
|
214
|
+
|
|
215
|
+
## Output Formats
|
|
216
|
+
|
|
217
|
+
### Table Output (Default)
|
|
218
|
+
|
|
219
|
+
Human-readable formatted table with:
|
|
220
|
+
- Overall coverage summary
|
|
221
|
+
- Per-file coverage metrics
|
|
222
|
+
- Priority scores
|
|
223
|
+
- Recommended focus areas
|
|
224
|
+
|
|
225
|
+
### JSON Output
|
|
226
|
+
|
|
227
|
+
Machine-readable format:
|
|
228
|
+
|
|
229
|
+
```json
|
|
230
|
+
{
|
|
231
|
+
"overall": {
|
|
232
|
+
"lines": { "found": 1572, "hit": 1234, "coverage": 78.5 },
|
|
233
|
+
"functions": { "found": 190, "hit": 156, "coverage": 82.1 },
|
|
234
|
+
"branches": { "found": 150, "hit": 98, "coverage": 65.3 },
|
|
235
|
+
"fileCount": 25
|
|
236
|
+
},
|
|
237
|
+
"files": [
|
|
238
|
+
{
|
|
239
|
+
"file": "src/auth/login.ts",
|
|
240
|
+
"lines": { "found": 218, "hit": 98, "coverage": 45.0 },
|
|
241
|
+
"functions": { "found": 10, "hit": 5, "coverage": 50.0 },
|
|
242
|
+
"branches": { "found": 20, "hit": 7, "coverage": 35.0 },
|
|
243
|
+
"priorityScore": 156.32,
|
|
244
|
+
"uncoveredLines": 120,
|
|
245
|
+
"uncoveredBranches": 13
|
|
246
|
+
}
|
|
247
|
+
]
|
|
248
|
+
}
|
|
249
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@redaksjon/brennpunkt",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Coverage priority analyzer - identify where to focus testing efforts",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"brennpunkt": "./dist/main.js",
|
|
9
|
+
"brennpunkt-mcp": "./dist/mcp/server.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"guide",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"exports": {
|
|
17
|
+
".": "./dist/index.js",
|
|
18
|
+
"./mcp": "./dist/mcp/server.js"
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/redaksjon/brennpunkt.git"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/redaksjon/brennpunkt",
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "npm run lint && tsc --noEmit && vite build && chmod +x dist/main.js dist/mcp/server.js",
|
|
27
|
+
"dev": "vite",
|
|
28
|
+
"watch": "vite build --watch",
|
|
29
|
+
"test": "vitest run --coverage",
|
|
30
|
+
"test:coverage": "vitest run --coverage",
|
|
31
|
+
"lint": "eslint . --ext .ts",
|
|
32
|
+
"lint:fix": "eslint . --ext .ts --fix",
|
|
33
|
+
"clean": "rm -rf dist",
|
|
34
|
+
"precommit": "npm run lint && npm run test",
|
|
35
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"coverage",
|
|
39
|
+
"testing",
|
|
40
|
+
"lcov",
|
|
41
|
+
"analysis",
|
|
42
|
+
"priority",
|
|
43
|
+
"mcp",
|
|
44
|
+
"ai",
|
|
45
|
+
"cursor",
|
|
46
|
+
"claude",
|
|
47
|
+
"agentic"
|
|
48
|
+
],
|
|
49
|
+
"author": "Tim O'Brien <tobrien@discursive.com>",
|
|
50
|
+
"license": "Apache-2.0",
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=18.0.0"
|
|
53
|
+
},
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
56
|
+
"commander": "^14.0.2",
|
|
57
|
+
"zod": "^4.1.12"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@eslint/eslintrc": "^3.3.1",
|
|
61
|
+
"@eslint/js": "^9.39.1",
|
|
62
|
+
"@rollup/plugin-replace": "^6.0.3",
|
|
63
|
+
"@swc/core": "^1.15.1",
|
|
64
|
+
"@types/node": "^24.10.1",
|
|
65
|
+
"@typescript-eslint/eslint-plugin": "^8.46.4",
|
|
66
|
+
"@typescript-eslint/parser": "^8.46.4",
|
|
67
|
+
"@vitest/coverage-v8": "^4.0.8",
|
|
68
|
+
"eslint": "^9.39.1",
|
|
69
|
+
"eslint-plugin-import": "^2.32.0",
|
|
70
|
+
"globals": "^17.0.0",
|
|
71
|
+
"rollup-plugin-preserve-shebang": "^1.0.1",
|
|
72
|
+
"typescript": "^5.9.3",
|
|
73
|
+
"vite": "^7.2.2",
|
|
74
|
+
"vite-plugin-node": "^7.0.0",
|
|
75
|
+
"vitest": "^4.0.8"
|
|
76
|
+
}
|
|
77
|
+
}
|