@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/guide/api.md ADDED
@@ -0,0 +1,324 @@
1
+ # Programmatic API
2
+
3
+ Use brennpunkt as a library in your Node.js applications.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @redaksjon/brennpunkt
9
+ ```
10
+
11
+ ## Basic Usage
12
+
13
+ ```typescript
14
+ import { parseLcov, analyzeCoverage, formatJson, formatTable } from '@redaksjon/brennpunkt';
15
+ import { readFileSync } from 'node:fs';
16
+
17
+ // Read coverage file
18
+ const lcovContent = readFileSync('coverage/lcov.info', 'utf-8');
19
+
20
+ // Parse LCOV format
21
+ const files = parseLcov(lcovContent);
22
+
23
+ // Analyze with default weights
24
+ const result = analyzeCoverage(
25
+ files,
26
+ { branches: 0.5, functions: 0.3, lines: 0.2 }, // weights
27
+ 10, // minLines
28
+ null // top (null = all files)
29
+ );
30
+
31
+ // Output as JSON
32
+ console.log(formatJson(result));
33
+
34
+ // Or as formatted table
35
+ console.log(formatTable(result, {
36
+ coveragePath: 'coverage/lcov.info',
37
+ weights: { branches: 0.5, functions: 0.3, lines: 0.2 },
38
+ minLines: 10,
39
+ json: false,
40
+ top: null
41
+ }));
42
+ ```
43
+
44
+ ## API Reference
45
+
46
+ ### `parseLcov(content: string): FileCoverage[]`
47
+
48
+ Parse LCOV format content into structured coverage data.
49
+
50
+ **Parameters**:
51
+ - `content`: Raw LCOV file content as string
52
+
53
+ **Returns**: Array of `FileCoverage` objects
54
+
55
+ ```typescript
56
+ interface FileCoverage {
57
+ file: string;
58
+ linesFound: number;
59
+ linesHit: number;
60
+ functionsFound: number;
61
+ functionsHit: number;
62
+ branchesFound: number;
63
+ branchesHit: number;
64
+ }
65
+ ```
66
+
67
+ **Example**:
68
+
69
+ ```typescript
70
+ const lcov = `SF:src/index.ts
71
+ LF:100
72
+ LH:80
73
+ FNF:10
74
+ FNH:8
75
+ BRF:20
76
+ BRH:15
77
+ end_of_record`;
78
+
79
+ const files = parseLcov(lcov);
80
+ // [{ file: 'src/index.ts', linesFound: 100, linesHit: 80, ... }]
81
+ ```
82
+
83
+ ### `analyzeCoverage(files, weights, minLines, top): AnalysisResult`
84
+
85
+ Analyze coverage data and calculate priority scores.
86
+
87
+ **Parameters**:
88
+ - `files`: Array of `FileCoverage` from `parseLcov()`
89
+ - `weights`: `PriorityWeights` object
90
+ - `minLines`: Minimum lines threshold (number)
91
+ - `top`: Limit to top N files (number or null)
92
+
93
+ ```typescript
94
+ interface PriorityWeights {
95
+ branches: number;
96
+ functions: number;
97
+ lines: number;
98
+ }
99
+ ```
100
+
101
+ **Returns**: `AnalysisResult` object
102
+
103
+ ```typescript
104
+ interface AnalysisResult {
105
+ overall: {
106
+ lines: { found: number; hit: number; coverage: number };
107
+ functions: { found: number; hit: number; coverage: number };
108
+ branches: { found: number; hit: number; coverage: number };
109
+ fileCount: number;
110
+ };
111
+ files: AnalyzedFile[];
112
+ }
113
+
114
+ interface AnalyzedFile {
115
+ file: string;
116
+ lines: { found: number; hit: number; coverage: number };
117
+ functions: { found: number; hit: number; coverage: number };
118
+ branches: { found: number; hit: number; coverage: number };
119
+ priorityScore: number;
120
+ uncoveredLines: number;
121
+ uncoveredBranches: number;
122
+ }
123
+ ```
124
+
125
+ **Example**:
126
+
127
+ ```typescript
128
+ const result = analyzeCoverage(
129
+ files,
130
+ { branches: 0.5, functions: 0.3, lines: 0.2 },
131
+ 10,
132
+ 5 // Top 5 only
133
+ );
134
+
135
+ console.log(result.overall.lines.coverage); // 85.5
136
+ console.log(result.files[0].priorityScore); // 156.3
137
+ ```
138
+
139
+ ### `formatJson(result: AnalysisResult): string`
140
+
141
+ Format analysis result as JSON string.
142
+
143
+ **Parameters**:
144
+ - `result`: `AnalysisResult` from `analyzeCoverage()`
145
+
146
+ **Returns**: Formatted JSON string
147
+
148
+ ```typescript
149
+ const jsonOutput = formatJson(result);
150
+ console.log(jsonOutput);
151
+ // { "overall": { ... }, "files": [ ... ] }
152
+ ```
153
+
154
+ ### `formatTable(result, options): string`
155
+
156
+ Format analysis result as human-readable table.
157
+
158
+ **Parameters**:
159
+ - `result`: `AnalysisResult` from `analyzeCoverage()`
160
+ - `options`: `AnalyzerOptions` object
161
+
162
+ **Returns**: Formatted table string with ANSI colors
163
+
164
+ ```typescript
165
+ interface AnalyzerOptions {
166
+ coveragePath: string;
167
+ weights: PriorityWeights;
168
+ minLines: number;
169
+ json: boolean;
170
+ top: number | null;
171
+ }
172
+ ```
173
+
174
+ ### `discoverCoverageFile(): { found: string; searched: string[] } | null`
175
+
176
+ Search for coverage file in common locations.
177
+
178
+ **Returns**: Object with found path and searched paths, or null if not found
179
+
180
+ ```typescript
181
+ const discovery = discoverCoverageFile();
182
+ if (discovery) {
183
+ console.log(`Found: ${discovery.found}`);
184
+ console.log(`Searched: ${discovery.searched.join(', ')}`);
185
+ } else {
186
+ console.log('No coverage file found');
187
+ }
188
+ ```
189
+
190
+ ## Advanced Examples
191
+
192
+ ### Custom Analysis Pipeline
193
+
194
+ ```typescript
195
+ import { parseLcov, analyzeCoverage } from '@redaksjon/brennpunkt';
196
+ import { readFileSync, writeFileSync } from 'node:fs';
197
+
198
+ async function runAnalysis() {
199
+ // Read coverage
200
+ const lcov = readFileSync('coverage/lcov.info', 'utf-8');
201
+ const files = parseLcov(lcov);
202
+
203
+ // Filter specific directories
204
+ const srcFiles = files.filter(f => f.file.startsWith('src/'));
205
+
206
+ // Analyze with custom weights
207
+ const result = analyzeCoverage(
208
+ srcFiles,
209
+ { branches: 0.7, functions: 0.2, lines: 0.1 },
210
+ 20,
211
+ 10
212
+ );
213
+
214
+ // Process results
215
+ const criticalFiles = result.files.filter(f => f.priorityScore > 100);
216
+
217
+ if (criticalFiles.length > 0) {
218
+ console.error('Critical coverage gaps:');
219
+ criticalFiles.forEach(f => {
220
+ console.error(` - ${f.file}: ${f.priorityScore.toFixed(1)}`);
221
+ });
222
+ process.exit(1);
223
+ }
224
+
225
+ // Save report
226
+ writeFileSync('coverage-analysis.json', JSON.stringify(result, null, 2));
227
+ }
228
+
229
+ runAnalysis();
230
+ ```
231
+
232
+ ### Integration with Test Framework
233
+
234
+ ```typescript
235
+ // vitest.config.ts
236
+ import { defineConfig } from 'vitest/config';
237
+
238
+ export default defineConfig({
239
+ test: {
240
+ coverage: {
241
+ reporter: ['lcov', 'text'],
242
+ },
243
+ onFinished: async () => {
244
+ const { parseLcov, analyzeCoverage, formatTable } = await import('@redaksjon/brennpunkt');
245
+ const { readFileSync } = await import('node:fs');
246
+
247
+ const lcov = readFileSync('coverage/lcov.info', 'utf-8');
248
+ const files = parseLcov(lcov);
249
+ const result = analyzeCoverage(
250
+ files,
251
+ { branches: 0.5, functions: 0.3, lines: 0.2 },
252
+ 10,
253
+ 5
254
+ );
255
+
256
+ console.log('\n' + formatTable(result, {
257
+ coveragePath: 'coverage/lcov.info',
258
+ weights: { branches: 0.5, functions: 0.3, lines: 0.2 },
259
+ minLines: 10,
260
+ json: false,
261
+ top: 5
262
+ }));
263
+ }
264
+ }
265
+ });
266
+ ```
267
+
268
+ ### AI/LLM Integration
269
+
270
+ ```typescript
271
+ import { parseLcov, analyzeCoverage } from '@redaksjon/brennpunkt';
272
+ import { readFileSync } from 'node:fs';
273
+
274
+ // Get actionable data for AI assistants
275
+ function getCoverageTargets(topN = 3) {
276
+ const lcov = readFileSync('coverage/lcov.info', 'utf-8');
277
+ const files = parseLcov(lcov);
278
+ const result = analyzeCoverage(
279
+ files,
280
+ { branches: 0.5, functions: 0.3, lines: 0.2 },
281
+ 10,
282
+ topN
283
+ );
284
+
285
+ return result.files.map(f => ({
286
+ file: f.file,
287
+ priority: f.priorityScore,
288
+ missingBranches: f.uncoveredBranches,
289
+ missingLines: f.uncoveredLines,
290
+ branchCoverage: f.branches.coverage,
291
+ suggestion: f.branches.coverage < 50
292
+ ? 'Focus on branch coverage - test conditional logic'
293
+ : f.functions.coverage < 50
294
+ ? 'Focus on function coverage - test more entry points'
295
+ : 'General coverage improvement'
296
+ }));
297
+ }
298
+
299
+ // Example output for AI:
300
+ // [
301
+ // {
302
+ // file: 'src/auth/login.ts',
303
+ // priority: 156.3,
304
+ // missingBranches: 13,
305
+ // missingLines: 45,
306
+ // branchCoverage: 35,
307
+ // suggestion: 'Focus on branch coverage - test conditional logic'
308
+ // }
309
+ // ]
310
+ ```
311
+
312
+ ## TypeScript Types
313
+
314
+ All types are exported from the package:
315
+
316
+ ```typescript
317
+ import type {
318
+ FileCoverage,
319
+ AnalyzedFile,
320
+ AnalysisResult,
321
+ PriorityWeights,
322
+ AnalyzerOptions
323
+ } from '@redaksjon/brennpunkt';
324
+ ```
@@ -0,0 +1,235 @@
1
+ # Configuration
2
+
3
+ Brennpunkt can be configured via a YAML file in your project directory.
4
+
5
+ ## Configuration File
6
+
7
+ Create a `brennpunkt.yaml` file in your project root:
8
+
9
+ ```yaml
10
+ # Brennpunkt Configuration
11
+ # https://github.com/redaksjon/brennpunkt
12
+
13
+ # Path to lcov.info coverage file
14
+ coveragePath: coverage/lcov.info
15
+
16
+ # Priority weights for branches, functions, lines (should sum to 1.0)
17
+ # Higher branch weight = untested branches are prioritized more heavily
18
+ weights: "0.5,0.3,0.2"
19
+
20
+ # Minimum number of lines for a file to be included
21
+ # Helps filter out tiny utility files
22
+ minLines: 10
23
+
24
+ # Output format (true for JSON, false for table)
25
+ json: false
26
+
27
+ # Limit results to top N files (remove for all files)
28
+ top: 20
29
+ ```
30
+
31
+ ## Generate Default Config
32
+
33
+ Create a starter configuration file:
34
+
35
+ ```bash
36
+ brennpunkt --init-config
37
+ ```
38
+
39
+ This creates `brennpunkt.yaml` with all options commented out.
40
+
41
+ ## Custom Config Location
42
+
43
+ Specify a different config file:
44
+
45
+ ```bash
46
+ # Use config in .config directory
47
+ brennpunkt --config .config/brennpunkt.yaml
48
+
49
+ # Generate config at specific path
50
+ brennpunkt --init-config --config .config/brennpunkt.yaml
51
+ ```
52
+
53
+ ## Configuration Options
54
+
55
+ ### `coveragePath`
56
+
57
+ **Type**: `string`
58
+ **Default**: Auto-discovered
59
+
60
+ Path to the lcov.info coverage file. If not specified, brennpunkt searches common locations.
61
+
62
+ ```yaml
63
+ coveragePath: coverage/lcov.info
64
+ ```
65
+
66
+ ### `weights`
67
+
68
+ **Type**: `string` (comma-separated numbers)
69
+ **Default**: `"0.5,0.3,0.2"`
70
+
71
+ Priority weights for branches, functions, and lines. Should sum to 1.0.
72
+
73
+ ```yaml
74
+ # Branch-heavy (for complex conditional logic)
75
+ weights: "0.7,0.2,0.1"
76
+
77
+ # Equal weights
78
+ weights: "0.33,0.33,0.34"
79
+
80
+ # Function-heavy (for finding dead code)
81
+ weights: "0.2,0.6,0.2"
82
+ ```
83
+
84
+ ### `minLines`
85
+
86
+ **Type**: `number`
87
+ **Default**: `10`
88
+
89
+ Exclude files with fewer than this many lines. Helps filter noise from tiny files.
90
+
91
+ ```yaml
92
+ # Default
93
+ minLines: 10
94
+
95
+ # More aggressive filtering
96
+ minLines: 50
97
+
98
+ # Include everything
99
+ minLines: 0
100
+ ```
101
+
102
+ ### `json`
103
+
104
+ **Type**: `boolean`
105
+ **Default**: `false`
106
+
107
+ Output as JSON instead of formatted table.
108
+
109
+ ```yaml
110
+ json: true
111
+ ```
112
+
113
+ ### `top`
114
+
115
+ **Type**: `number`
116
+ **Default**: (all files)
117
+
118
+ Limit output to top N priority files.
119
+
120
+ ```yaml
121
+ top: 20
122
+ ```
123
+
124
+ ## Configuration Precedence
125
+
126
+ Configuration is resolved in this order (highest priority first):
127
+
128
+ 1. **CLI arguments** - Always override everything
129
+ 2. **Config file** - `brennpunkt.yaml` in current directory
130
+ 3. **Built-in defaults** - Fallback values
131
+
132
+ **Example**:
133
+
134
+ ```yaml
135
+ # brennpunkt.yaml
136
+ weights: "0.6,0.2,0.2"
137
+ top: 20
138
+ ```
139
+
140
+ ```bash
141
+ # CLI overrides config file
142
+ brennpunkt --top 5
143
+ # Uses: weights from config (0.6,0.2,0.2), top=5 from CLI
144
+ ```
145
+
146
+ ## Check Configuration
147
+
148
+ View resolved configuration with source tracking:
149
+
150
+ ```bash
151
+ brennpunkt --check-config
152
+ ```
153
+
154
+ Output:
155
+
156
+ ```
157
+ ================================================================================
158
+ BRENNPUNKT CONFIGURATION
159
+ ================================================================================
160
+
161
+ Config file: brennpunkt.yaml
162
+ Status: Found
163
+
164
+ RESOLVED CONFIGURATION:
165
+ --------------------------------------------------------------------------------
166
+ [config file] coveragePath : "coverage/lcov.info"
167
+ [config file] weights : "0.6,0.2,0.2"
168
+ [config file] minLines : 20
169
+ [default] json : false
170
+ [config file] top : 10
171
+
172
+ ================================================================================
173
+ ```
174
+
175
+ ## Environment-Specific Configs
176
+
177
+ For different environments, use the `--config` flag:
178
+
179
+ ```bash
180
+ # Development (verbose)
181
+ brennpunkt --config .config/brennpunkt.dev.yaml
182
+
183
+ # CI (JSON output)
184
+ brennpunkt --config .config/brennpunkt.ci.yaml
185
+ ```
186
+
187
+ Example CI config:
188
+
189
+ ```yaml
190
+ # .config/brennpunkt.ci.yaml
191
+ json: true
192
+ top: 10
193
+ minLines: 20
194
+ ```
195
+
196
+ ## MCP Server Configuration
197
+
198
+ The MCP server automatically loads each project's `brennpunkt.yaml`:
199
+
200
+ - Pass `projectPath` as a parameter to each MCP tool call
201
+ - The server reads `{projectPath}/brennpunkt.yaml` if it exists
202
+ - Responses include `configUsed: "brennpunkt.yaml"` or `configUsed: "defaults"`
203
+ - No per-project MCP server configuration needed
204
+
205
+ This means different projects can have different analysis settings (weights, minLines, etc.) without any server reconfiguration.
206
+
207
+ ## Monorepo Usage
208
+
209
+ For monorepos, place config files in each package:
210
+
211
+ ```
212
+ packages/
213
+ ├── auth/
214
+ │ ├── brennpunkt.yaml
215
+ │ └── coverage/lcov.info
216
+ ├── api/
217
+ │ ├── brennpunkt.yaml
218
+ │ └── coverage/lcov.info
219
+ └── shared/
220
+ ├── brennpunkt.yaml
221
+ └── coverage/lcov.info
222
+ ```
223
+
224
+ Run from each package directory:
225
+
226
+ ```bash
227
+ cd packages/auth && brennpunkt
228
+ cd packages/api && brennpunkt
229
+ ```
230
+
231
+ Or specify paths:
232
+
233
+ ```bash
234
+ brennpunkt --config packages/auth/brennpunkt.yaml packages/auth/coverage/lcov.info
235
+ ```