sigmap 3.6.0 → 4.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/AGENTS.md +119 -98
- package/gen-context.js +128 -17
- package/package.json +1 -1
- package/packages/adapters/claude.js +12 -1
- package/packages/adapters/copilot.js +12 -1
- package/packages/adapters/cursor.js +12 -1
- package/packages/adapters/gemini.js +11 -0
- package/packages/adapters/openai.js +11 -0
- package/packages/adapters/windsurf.js +12 -1
- package/packages/cli/package.json +1 -1
- package/packages/core/package.json +1 -1
- package/src/analysis/coverage-score.js +85 -0
- package/src/eval/analyzer.js +5 -0
- package/src/mcp/server.js +1 -1
package/AGENTS.md
CHANGED
|
@@ -12,35 +12,40 @@ Use this marker block for all appendable context files:
|
|
|
12
12
|
## Auto-generated signatures
|
|
13
13
|
<!-- Updated by gen-context.js -->
|
|
14
14
|
You are a coding assistant with full knowledge of this codebase.
|
|
15
|
-
Below are the code signatures extracted by SigMap
|
|
15
|
+
Below are the code signatures extracted by SigMap v4.0.0 on 2026-04-15T05:40:32.012Z.
|
|
16
16
|
|
|
17
17
|
Use these signatures to answer questions about the code accurately.
|
|
18
18
|
|
|
19
19
|
## Code Signatures
|
|
20
20
|
|
|
21
|
-
<!-- Generated by SigMap gen-context.js
|
|
21
|
+
<!-- Generated by SigMap gen-context.js v4.0.0 -->
|
|
22
22
|
<!-- DO NOT EDIT below the marker line — run gen-context.js to regenerate -->
|
|
23
23
|
|
|
24
24
|
# Code signatures
|
|
25
25
|
|
|
26
|
+
## changes (last 5 commits — 6 hours ago)
|
|
27
|
+
```
|
|
28
|
+
src/extractors/generic.js +extract
|
|
29
|
+
src/format/llm-txt.js +outputPath +format
|
|
30
|
+
src/format/llms-txt.js +outputPath +getShortCommit +detectVersion +format
|
|
31
|
+
packages/adapters/llm-full.js +outputPath +format +write
|
|
32
|
+
```
|
|
33
|
+
|
|
26
34
|
## packages
|
|
27
35
|
|
|
28
|
-
### packages/
|
|
36
|
+
### packages/adapters/llm-full.js
|
|
29
37
|
```
|
|
30
|
-
module.exports = {
|
|
31
|
-
function
|
|
32
|
-
function
|
|
33
|
-
function
|
|
34
|
-
function buildSigIndex(cwd) → Map<string, string[]>
|
|
35
|
-
function scan(sigs, filePath) → { safe: string[], redacte
|
|
36
|
-
function score(cwd) → { * score: number, * grad
|
|
37
|
-
function adapt(context, adapterName, opts = {}) → string
|
|
38
|
+
module.exports = { name: 'llm-full', format, outputPath, write }
|
|
39
|
+
function outputPath(cwd)
|
|
40
|
+
function format(context, opts)
|
|
41
|
+
function write(context, cwd, opts)
|
|
38
42
|
```
|
|
39
43
|
|
|
40
44
|
### packages/adapters/claude.js
|
|
41
45
|
```
|
|
42
46
|
module.exports = { name, format, outputPath, write }
|
|
43
47
|
function format(context, opts = {}) → string
|
|
48
|
+
function _confidenceMeta(opts)
|
|
44
49
|
function outputPath(cwd) → string
|
|
45
50
|
function write(context, cwd, opts = {})
|
|
46
51
|
```
|
|
@@ -57,6 +62,7 @@ function write(context, cwd, opts = {})
|
|
|
57
62
|
```
|
|
58
63
|
module.exports = { name, format, outputPath, write }
|
|
59
64
|
function format(context, opts = {}) → string
|
|
65
|
+
function _confidenceMeta(opts)
|
|
60
66
|
function outputPath(cwd) → string
|
|
61
67
|
function write(context, cwd, opts = {})
|
|
62
68
|
```
|
|
@@ -65,6 +71,7 @@ function write(context, cwd, opts = {})
|
|
|
65
71
|
```
|
|
66
72
|
module.exports = { name, format, outputPath }
|
|
67
73
|
function format(context, opts = {}) → string
|
|
74
|
+
function _confidenceMeta(opts)
|
|
68
75
|
function outputPath(cwd) → string
|
|
69
76
|
```
|
|
70
77
|
|
|
@@ -74,6 +81,7 @@ module.exports = { name, format, outputPath, write }
|
|
|
74
81
|
function format(context, opts = {}) → string
|
|
75
82
|
function outputPath(cwd) → string
|
|
76
83
|
function write(context, cwd, opts = {})
|
|
84
|
+
function _confidenceMeta(opts)
|
|
77
85
|
```
|
|
78
86
|
|
|
79
87
|
### packages/adapters/index.js
|
|
@@ -85,25 +93,19 @@ function adapt(context, adapterName, opts = {}) → string
|
|
|
85
93
|
function outputsToAdapters(outputs) → string[]
|
|
86
94
|
```
|
|
87
95
|
|
|
88
|
-
### packages/adapters/llm-full.js
|
|
89
|
-
```
|
|
90
|
-
module.exports = { name: 'llm-full', format, outputPath, write }
|
|
91
|
-
function outputPath(cwd)
|
|
92
|
-
function format(context, opts)
|
|
93
|
-
function write(context, cwd, opts)
|
|
94
|
-
```
|
|
95
|
-
|
|
96
96
|
### packages/adapters/openai.js
|
|
97
97
|
```
|
|
98
98
|
module.exports = { name, format, outputPath }
|
|
99
99
|
function format(context, opts = {}) → string
|
|
100
100
|
function outputPath(cwd) → string
|
|
101
|
+
function _confidenceMeta(opts)
|
|
101
102
|
```
|
|
102
103
|
|
|
103
104
|
### packages/adapters/windsurf.js
|
|
104
105
|
```
|
|
105
106
|
module.exports = { name, format, outputPath }
|
|
106
107
|
function format(context, opts = {}) → string
|
|
108
|
+
function _confidenceMeta(opts)
|
|
107
109
|
function outputPath(cwd) → string
|
|
108
110
|
```
|
|
109
111
|
|
|
@@ -133,8 +135,49 @@ code-fence js
|
|
|
133
135
|
code-fence ---
|
|
134
136
|
```
|
|
135
137
|
|
|
138
|
+
### packages/core/index.js
|
|
139
|
+
```
|
|
140
|
+
module.exports = { extract, rank, buildSigIndex, scan, score, adapt }
|
|
141
|
+
function _resolveExtractor(language)
|
|
142
|
+
function extract(src, language) → string[]
|
|
143
|
+
function rank(query, sigIndex, opts) → { file: string, score: nu
|
|
144
|
+
function buildSigIndex(cwd) → Map<string, string[]>
|
|
145
|
+
function scan(sigs, filePath) → { safe: string[], redacte
|
|
146
|
+
function score(cwd) → { * score: number, * grad
|
|
147
|
+
function adapt(context, adapterName, opts = {}) → string
|
|
148
|
+
```
|
|
149
|
+
|
|
136
150
|
## src
|
|
137
151
|
|
|
152
|
+
### src/extractors/generic.js
|
|
153
|
+
```
|
|
154
|
+
module.exports = { extract }
|
|
155
|
+
function extract(src)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### src/format/llm-txt.js
|
|
159
|
+
```
|
|
160
|
+
module.exports = { format, outputPath }
|
|
161
|
+
function outputPath(cwd)
|
|
162
|
+
function format(context, cwd, version)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### src/format/llms-txt.js
|
|
166
|
+
```
|
|
167
|
+
module.exports = { format, outputPath }
|
|
168
|
+
function outputPath(cwd)
|
|
169
|
+
function getShortCommit(cwd)
|
|
170
|
+
function detectVersion(cwd)
|
|
171
|
+
function format(context, cwd, writtenFiles, sigmapVersion)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### src/analysis/coverage-score.js
|
|
175
|
+
```
|
|
176
|
+
module.exports = { coverageScore }
|
|
177
|
+
function coverageScore(cwd, fileEntries, config) → { * score: number, * grad
|
|
178
|
+
function _walk(dir, excludeSet, out)
|
|
179
|
+
```
|
|
180
|
+
|
|
138
181
|
### src/config/defaults.js
|
|
139
182
|
```
|
|
140
183
|
module.exports = { DEFAULTS }
|
|
@@ -161,63 +204,6 @@ function formatAnalysisTable(stats, showSlow) → string
|
|
|
161
204
|
function formatAnalysisJSON(stats) → object
|
|
162
205
|
```
|
|
163
206
|
|
|
164
|
-
### src/extractors/markdown.js
|
|
165
|
-
```
|
|
166
|
-
module.exports = { extract }
|
|
167
|
-
function extract(src) → string[]
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
### src/extractors/patterns.js
|
|
171
|
-
```
|
|
172
|
-
module.exports = { extract }
|
|
173
|
-
function extract(src) → string[]
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
### src/extractors/properties.js
|
|
177
|
-
```
|
|
178
|
-
module.exports = { extract }
|
|
179
|
-
function extract(src) → string[]
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
### src/extractors/python_dataclass.js
|
|
183
|
-
```
|
|
184
|
-
module.exports = { extract }
|
|
185
|
-
function extract(src) → string[]
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
### src/extractors/toml.js
|
|
189
|
-
```
|
|
190
|
-
module.exports = { extract }
|
|
191
|
-
function extract(src) → string[]
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
### src/extractors/typescript_react.js
|
|
195
|
-
```
|
|
196
|
-
module.exports = { extract }
|
|
197
|
-
function extract(src) → string[]
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
### src/extractors/vue_sfc.js
|
|
201
|
-
```
|
|
202
|
-
module.exports = { extract }
|
|
203
|
-
function extract(src) → string[]
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
### src/extractors/xml.js
|
|
207
|
-
```
|
|
208
|
-
module.exports = { extract }
|
|
209
|
-
function extract(src) → string[]
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
### src/mcp/server.js
|
|
213
|
-
```
|
|
214
|
-
module.exports = { start }
|
|
215
|
-
function respond(id, result)
|
|
216
|
-
function respondError(id, code, message)
|
|
217
|
-
function dispatch(msg, cwd)
|
|
218
|
-
function start(cwd)
|
|
219
|
-
```
|
|
220
|
-
|
|
221
207
|
### src/eval/runner.js
|
|
222
208
|
```
|
|
223
209
|
module.exports = { run, rank, loadTasks, buildSigIndex, formatTable, formatMetrics, tokenize }
|
|
@@ -301,12 +287,6 @@ module.exports = { extract }
|
|
|
301
287
|
function extract(src) → string[]
|
|
302
288
|
```
|
|
303
289
|
|
|
304
|
-
### src/extractors/generic.js
|
|
305
|
-
```
|
|
306
|
-
module.exports = { extract }
|
|
307
|
-
function extract(src)
|
|
308
|
-
```
|
|
309
|
-
|
|
310
290
|
### src/extractors/go.js
|
|
311
291
|
```
|
|
312
292
|
module.exports = { extract }
|
|
@@ -359,6 +339,18 @@ function extractMembers(block)
|
|
|
359
339
|
function normalizeParams(params)
|
|
360
340
|
```
|
|
361
341
|
|
|
342
|
+
### src/extractors/markdown.js
|
|
343
|
+
```
|
|
344
|
+
module.exports = { extract }
|
|
345
|
+
function extract(src) → string[]
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### src/extractors/patterns.js
|
|
349
|
+
```
|
|
350
|
+
module.exports = { extract }
|
|
351
|
+
function extract(src) → string[]
|
|
352
|
+
```
|
|
353
|
+
|
|
362
354
|
### src/extractors/php.js
|
|
363
355
|
```
|
|
364
356
|
module.exports = { extract }
|
|
@@ -376,6 +368,12 @@ function diffSignatures(baseSigs, currentSigs) → {added:string[], removed:
|
|
|
376
368
|
function extractName(sig)
|
|
377
369
|
```
|
|
378
370
|
|
|
371
|
+
### src/extractors/properties.js
|
|
372
|
+
```
|
|
373
|
+
module.exports = { extract }
|
|
374
|
+
function extract(src) → string[]
|
|
375
|
+
```
|
|
376
|
+
|
|
379
377
|
### src/extractors/protobuf.js
|
|
380
378
|
```
|
|
381
379
|
module.exports = { extract }
|
|
@@ -395,6 +393,12 @@ function normalizeParams(params)
|
|
|
395
393
|
function extractDocHint(src, fnName, fnSigLine)
|
|
396
394
|
```
|
|
397
395
|
|
|
396
|
+
### src/extractors/python_dataclass.js
|
|
397
|
+
```
|
|
398
|
+
module.exports = { extract }
|
|
399
|
+
function extract(src) → string[]
|
|
400
|
+
```
|
|
401
|
+
|
|
398
402
|
### src/extractors/ruby.js
|
|
399
403
|
```
|
|
400
404
|
module.exports = { extract }
|
|
@@ -467,6 +471,12 @@ module.exports = { extractTodos }
|
|
|
467
471
|
function extractTodos(src) → {line:number, tag:string,
|
|
468
472
|
```
|
|
469
473
|
|
|
474
|
+
### src/extractors/toml.js
|
|
475
|
+
```
|
|
476
|
+
module.exports = { extract }
|
|
477
|
+
function extract(src) → string[]
|
|
478
|
+
```
|
|
479
|
+
|
|
470
480
|
### src/extractors/typescript.js
|
|
471
481
|
```
|
|
472
482
|
module.exports = { extract }
|
|
@@ -477,6 +487,12 @@ function extractClassMembers(block)
|
|
|
477
487
|
function normalizeParams(params)
|
|
478
488
|
```
|
|
479
489
|
|
|
490
|
+
### src/extractors/typescript_react.js
|
|
491
|
+
```
|
|
492
|
+
module.exports = { extract }
|
|
493
|
+
function extract(src) → string[]
|
|
494
|
+
```
|
|
495
|
+
|
|
480
496
|
### src/extractors/vue.js
|
|
481
497
|
```
|
|
482
498
|
module.exports = { extract }
|
|
@@ -485,6 +501,18 @@ function normalizeParams(params)
|
|
|
485
501
|
function normalizeType(type)
|
|
486
502
|
```
|
|
487
503
|
|
|
504
|
+
### src/extractors/vue_sfc.js
|
|
505
|
+
```
|
|
506
|
+
module.exports = { extract }
|
|
507
|
+
function extract(src) → string[]
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
### src/extractors/xml.js
|
|
511
|
+
```
|
|
512
|
+
module.exports = { extract }
|
|
513
|
+
function extract(src) → string[]
|
|
514
|
+
```
|
|
515
|
+
|
|
488
516
|
### src/extractors/yaml.js
|
|
489
517
|
```
|
|
490
518
|
module.exports = { extract }
|
|
@@ -518,22 +546,6 @@ function generateDashboardHtml(cwd, health)
|
|
|
518
546
|
function renderHistoryCharts(cwd, health)
|
|
519
547
|
```
|
|
520
548
|
|
|
521
|
-
### src/format/llm-txt.js
|
|
522
|
-
```
|
|
523
|
-
module.exports = { format, outputPath }
|
|
524
|
-
function outputPath(cwd)
|
|
525
|
-
function format(context, cwd, version)
|
|
526
|
-
```
|
|
527
|
-
|
|
528
|
-
### src/format/llms-txt.js
|
|
529
|
-
```
|
|
530
|
-
module.exports = { format, outputPath }
|
|
531
|
-
function outputPath(cwd)
|
|
532
|
-
function getShortCommit(cwd)
|
|
533
|
-
function detectVersion(cwd)
|
|
534
|
-
function format(context, cwd, writtenFiles, sigmapVersion)
|
|
535
|
-
```
|
|
536
|
-
|
|
537
549
|
### src/graph/builder.js
|
|
538
550
|
```
|
|
539
551
|
module.exports = { build, buildFromCwd, extractFileDeps }
|
|
@@ -597,6 +609,15 @@ function queryContext(args, cwd)
|
|
|
597
609
|
function getImpact(args, cwd)
|
|
598
610
|
```
|
|
599
611
|
|
|
612
|
+
### src/mcp/server.js
|
|
613
|
+
```
|
|
614
|
+
module.exports = { start }
|
|
615
|
+
function respond(id, result)
|
|
616
|
+
function respondError(id, code, message)
|
|
617
|
+
function dispatch(msg, cwd)
|
|
618
|
+
function start(cwd)
|
|
619
|
+
```
|
|
620
|
+
|
|
600
621
|
### src/mcp/tools.js
|
|
601
622
|
```
|
|
602
623
|
module.exports = { TOOLS }
|
package/gen-context.js
CHANGED
|
@@ -4478,7 +4478,7 @@ __factories["./src/mcp/server"] = function(module, exports) {
|
|
|
4478
4478
|
|
|
4479
4479
|
const SERVER_INFO = {
|
|
4480
4480
|
name: 'sigmap',
|
|
4481
|
-
version: '
|
|
4481
|
+
version: '4.0.0',
|
|
4482
4482
|
description: 'SigMap MCP server — code signatures on demand',
|
|
4483
4483
|
};
|
|
4484
4484
|
|
|
@@ -6040,7 +6040,7 @@ const path = require('path');
|
|
|
6040
6040
|
const os = require('os');
|
|
6041
6041
|
const { execSync } = require('child_process');
|
|
6042
6042
|
|
|
6043
|
-
const VERSION = '
|
|
6043
|
+
const VERSION = '4.0.0';
|
|
6044
6044
|
const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
|
|
6045
6045
|
|
|
6046
6046
|
function requireSourceOrBundled(key) {
|
|
@@ -6276,13 +6276,17 @@ function applyTokenBudget(fileEntries, maxTokens) {
|
|
|
6276
6276
|
else if (isTestFile(e.filePath)) { priority = 8; dropReason = 'budget: test file'; }
|
|
6277
6277
|
else if (isConfigFile(e.filePath)) { priority = 6; dropReason = 'budget: config file'; }
|
|
6278
6278
|
else priority = 4;
|
|
6279
|
-
|
|
6279
|
+
// v4.0: signal quality = sigs per line-of-code (higher = more informative)
|
|
6280
|
+
const loc = e.content ? e.content.split('\n').length : 1;
|
|
6281
|
+
const signalQuality = loc > 0 ? (e.sigs ? e.sigs.length : 0) / loc : 0;
|
|
6282
|
+
return { ...e, priority, dropReason, signalQuality };
|
|
6280
6283
|
});
|
|
6281
6284
|
|
|
6282
|
-
// Within same priority
|
|
6285
|
+
// Within same priority: sort by mtime ascending (oldest first), then signalQuality ascending (least informative first)
|
|
6283
6286
|
withPriority.sort((a, b) => {
|
|
6284
6287
|
if (b.priority !== a.priority) return b.priority - a.priority;
|
|
6285
|
-
return (a.mtime || 0) - (b.mtime || 0);
|
|
6288
|
+
if ((a.mtime || 0) !== (b.mtime || 0)) return (a.mtime || 0) - (b.mtime || 0);
|
|
6289
|
+
return (a.signalQuality || 0) - (b.signalQuality || 0);
|
|
6286
6290
|
});
|
|
6287
6291
|
|
|
6288
6292
|
const kept = [];
|
|
@@ -6716,11 +6720,17 @@ function writeClaude(content, cwd) {
|
|
|
6716
6720
|
// ---------------------------------------------------------------------------
|
|
6717
6721
|
// Report
|
|
6718
6722
|
// ---------------------------------------------------------------------------
|
|
6719
|
-
function
|
|
6723
|
+
function _coverageBar(pct, width) {
|
|
6724
|
+
width = width || 16;
|
|
6725
|
+
const filled = Math.round(pct / 100 * width);
|
|
6726
|
+
return '\u2588'.repeat(filled) + '\u2591'.repeat(width - filled);
|
|
6727
|
+
}
|
|
6728
|
+
|
|
6729
|
+
function printReport(inputTokens, finalTokens, fileCount, droppedCount, asJson, budgetLimit, coverageResult) {
|
|
6720
6730
|
const reduction = inputTokens > 0 ? (100 - (finalTokens / inputTokens) * 100).toFixed(1) : 0;
|
|
6721
6731
|
const overBudget = finalTokens > (budgetLimit || 6000);
|
|
6722
6732
|
if (asJson) {
|
|
6723
|
-
|
|
6733
|
+
const payload = {
|
|
6724
6734
|
version: VERSION,
|
|
6725
6735
|
timestamp: new Date().toISOString(),
|
|
6726
6736
|
rawTokens: inputTokens,
|
|
@@ -6731,7 +6741,22 @@ function printReport(inputTokens, finalTokens, fileCount, droppedCount, asJson,
|
|
|
6731
6741
|
reductionPct: parseFloat(reduction),
|
|
6732
6742
|
overBudget,
|
|
6733
6743
|
budgetLimit: budgetLimit || 6000,
|
|
6734
|
-
}
|
|
6744
|
+
};
|
|
6745
|
+
if (coverageResult) {
|
|
6746
|
+
payload.coverage = {
|
|
6747
|
+
score: coverageResult.score,
|
|
6748
|
+
grade: coverageResult.grade,
|
|
6749
|
+
confidence: coverageResult.confidence,
|
|
6750
|
+
totalFiles: coverageResult.total,
|
|
6751
|
+
includedFiles: coverageResult.included,
|
|
6752
|
+
droppedFiles: coverageResult.dropped,
|
|
6753
|
+
perModule: Object.fromEntries(
|
|
6754
|
+
Array.from(coverageResult.perModule.entries())
|
|
6755
|
+
.map(([k, v]) => [k, v])
|
|
6756
|
+
),
|
|
6757
|
+
};
|
|
6758
|
+
}
|
|
6759
|
+
process.stdout.write(JSON.stringify(payload) + '\n');
|
|
6735
6760
|
// Exit 1 in CI if over budget — lets pipelines fail fast
|
|
6736
6761
|
if (overBudget) process.exitCode = 1;
|
|
6737
6762
|
} else {
|
|
@@ -6743,6 +6768,20 @@ function printReport(inputTokens, finalTokens, fileCount, droppedCount, asJson,
|
|
|
6743
6768
|
console.log(` output tokens : ~${finalTokens}`);
|
|
6744
6769
|
console.log(` budget limit : ${budgetLimit || 6000}`);
|
|
6745
6770
|
console.log(` reduction : ${reduction}%`);
|
|
6771
|
+
if (coverageResult) {
|
|
6772
|
+
console.log(` coverage : ${coverageResult.grade} (${coverageResult.score}%) — ${coverageResult.included} of ${coverageResult.total} source files included`);
|
|
6773
|
+
console.log(` confidence : ${coverageResult.confidence}`);
|
|
6774
|
+
if (coverageResult.perModule && coverageResult.perModule.size > 0) {
|
|
6775
|
+
console.log('');
|
|
6776
|
+
console.log(' Module Coverage:');
|
|
6777
|
+
for (const [dir, mod] of coverageResult.perModule) {
|
|
6778
|
+
if (mod.total === 0) continue;
|
|
6779
|
+
const bar = _coverageBar(mod.pct);
|
|
6780
|
+
const attention = mod.pct < 50 ? ' \u2190 attention needed' : '';
|
|
6781
|
+
console.log(` ${dir.padEnd(18)} ${bar} ${String(mod.pct).padStart(3)}% (${mod.included}/${mod.total} files)${attention}`);
|
|
6782
|
+
}
|
|
6783
|
+
}
|
|
6784
|
+
}
|
|
6746
6785
|
if (overBudget) console.warn(`[sigmap] WARNING: output (${finalTokens} tokens) exceeds budget (${budgetLimit || 6000})`);
|
|
6747
6786
|
}
|
|
6748
6787
|
}
|
|
@@ -7032,6 +7071,41 @@ function runDiff(cwd, config, stagedOnly, baseRef) {
|
|
|
7032
7071
|
const scope = baseRef ? `diff-vs-${baseRef}` : (stagedOnly ? 'staged' : 'diff');
|
|
7033
7072
|
console.warn(`[sigmap] ${scope} files: ${fileEntries.length}, diff tokens: ~${finalTokens}`);
|
|
7034
7073
|
|
|
7074
|
+
// v4.0: risk score per changed file
|
|
7075
|
+
try {
|
|
7076
|
+
const { buildFromCwd } = requireSourceOrBundled('./src/graph/builder');
|
|
7077
|
+
const graph = buildFromCwd(cwd, { silent: true });
|
|
7078
|
+
const reverseGraph = graph.reverse || new Map();
|
|
7079
|
+
|
|
7080
|
+
function _isRouteFile(f) {
|
|
7081
|
+
return /\/(routes?|pages?|controllers?|handlers?|api)\//i.test(f)
|
|
7082
|
+
|| /\.(route|page|controller|handler)\.\w+$/.test(f);
|
|
7083
|
+
}
|
|
7084
|
+
function _riskScore(filePath, sigs, revGraph) {
|
|
7085
|
+
let s = 0;
|
|
7086
|
+
if (sigs.some(sig => sig.includes('export') || sig.includes('module.exports'))) s += 2;
|
|
7087
|
+
const deps = revGraph.get(filePath) || new Set();
|
|
7088
|
+
if (deps.size > 3) s += 2;
|
|
7089
|
+
if (_isRouteFile(filePath)) s += 1;
|
|
7090
|
+
if (/\.(config|env|settings)\.\w+$/.test(filePath)) s += 1;
|
|
7091
|
+
return s >= 4 ? 'HIGH' : s >= 2 ? 'MEDIUM' : 'LOW';
|
|
7092
|
+
}
|
|
7093
|
+
|
|
7094
|
+
console.warn(`[sigmap] Risk: Changed files (${fileEntries.length}):`);
|
|
7095
|
+
for (const fe of fileEntries) {
|
|
7096
|
+
const level = _riskScore(fe.filePath, fe.sigs, reverseGraph);
|
|
7097
|
+
const rel = path.relative(cwd, fe.filePath).replace(/\\/g, '/');
|
|
7098
|
+
const deps = reverseGraph.get(fe.filePath) || new Set();
|
|
7099
|
+
const reasons = [];
|
|
7100
|
+
if (fe.sigs.some(s => s.includes('export') || s.includes('module.exports'))) reasons.push('exports public API');
|
|
7101
|
+
if (deps.size > 0) reasons.push(`${deps.size} downstream dependent${deps.size === 1 ? '' : 's'}`);
|
|
7102
|
+
if (_isRouteFile(fe.filePath)) reasons.push('route file');
|
|
7103
|
+
if (/\.(config|env|settings)\.\w+$/.test(fe.filePath)) reasons.push('config file');
|
|
7104
|
+
const label = level === 'HIGH' ? '[HIGH] ' : level === 'MEDIUM' ? '[MEDIUM]' : '[LOW] ';
|
|
7105
|
+
console.warn(` ${rel.padEnd(40)} ${label}${reasons.length ? ' — ' + reasons.join(', ') : ''}`);
|
|
7106
|
+
}
|
|
7107
|
+
} catch (_) {}
|
|
7108
|
+
|
|
7035
7109
|
if (process.argv.includes('--report')) {
|
|
7036
7110
|
// Also show what the full run would cost for comparison
|
|
7037
7111
|
const fullResult = runGenerate(cwd, config, true);
|
|
@@ -7176,11 +7250,17 @@ function runGenerate(cwd, config, reportMode, reportJson = false) {
|
|
|
7176
7250
|
const droppedCount = beforeCount - budgeted.length;
|
|
7177
7251
|
const content = formatOutput(budgeted, cwd, false, config, null);
|
|
7178
7252
|
const finalTokens = estimateTokens(content);
|
|
7179
|
-
|
|
7253
|
+
// v4.0: compute coverage score for --report heatmap
|
|
7254
|
+
let coverageResult = null;
|
|
7255
|
+
try {
|
|
7256
|
+
const { coverageScore } = requireSourceOrBundled('./src/analysis/coverage-score');
|
|
7257
|
+
coverageResult = coverageScore(cwd, budgeted, config);
|
|
7258
|
+
} catch (_) {}
|
|
7259
|
+
result = { inputTokenTotal, finalTokens, fileCount: beforeCount, droppedCount, coverageResult };
|
|
7180
7260
|
}
|
|
7181
7261
|
|
|
7182
7262
|
if (reportMode || process.argv.includes('--report')) {
|
|
7183
|
-
printReport(result.inputTokenTotal, result.finalTokens, result.fileCount, result.droppedCount, reportJson, config.maxTokens);
|
|
7263
|
+
printReport(result.inputTokenTotal, result.finalTokens, result.fileCount, result.droppedCount, reportJson, config.maxTokens, result.coverageResult);
|
|
7184
7264
|
}
|
|
7185
7265
|
|
|
7186
7266
|
// Usage tracking (v0.9) — optional append-only NDJSON log
|
|
@@ -7212,17 +7292,26 @@ function runGenerate(cwd, config, reportMode, reportJson = false) {
|
|
|
7212
7292
|
const pct = result.inputTokenTotal > 0
|
|
7213
7293
|
? Math.round((1 - result.finalTokens / result.inputTokenTotal) * 100)
|
|
7214
7294
|
: 0;
|
|
7215
|
-
|
|
7295
|
+
// v4.0: coverage score in post-run summary
|
|
7296
|
+
let coverageLine = '';
|
|
7297
|
+
try {
|
|
7298
|
+
const { coverageScore } = requireSourceOrBundled('./src/analysis/coverage-score');
|
|
7299
|
+
const cov = coverageScore(cwd, fileEntries, config);
|
|
7300
|
+
coverageLine = ` Coverage : ${cov.grade} (${cov.score}%) \u2014 ${cov.included} of ${cov.total} source files included`;
|
|
7301
|
+
} catch (_) {}
|
|
7302
|
+
const lines = [
|
|
7216
7303
|
bar,
|
|
7217
7304
|
` SigMap v${VERSION}`,
|
|
7218
7305
|
` Files scanned : ${result.fileCount}`,
|
|
7219
7306
|
` Symbols found : ${syms.toLocaleString()}`,
|
|
7220
7307
|
` Token reduction: ${pct}% (${result.inputTokenTotal.toLocaleString()} \u2192 ${result.finalTokens.toLocaleString()})`,
|
|
7221
|
-
|
|
7222
|
-
|
|
7223
|
-
|
|
7224
|
-
|
|
7225
|
-
|
|
7308
|
+
];
|
|
7309
|
+
if (coverageLine) lines.push(coverageLine);
|
|
7310
|
+
lines.push(` Output : .github/copilot-instructions.md`);
|
|
7311
|
+
lines.push(bar);
|
|
7312
|
+
lines.push(` Try: "explain the architecture" \u00b7 "find the auth module"`);
|
|
7313
|
+
lines.push(bar, '');
|
|
7314
|
+
process.stderr.write(lines.join('\n'));
|
|
7226
7315
|
}
|
|
7227
7316
|
|
|
7228
7317
|
return result;
|
|
@@ -7725,6 +7814,17 @@ function main() {
|
|
|
7725
7814
|
if (args.includes('--health')) {
|
|
7726
7815
|
const { score } = __require('./src/health/scorer');
|
|
7727
7816
|
const result = score(cwd);
|
|
7817
|
+
// v4.0: compute live coverage score to include in health output
|
|
7818
|
+
let coverageResult = null;
|
|
7819
|
+
try {
|
|
7820
|
+
const { coverageScore } = requireSourceOrBundled('./src/analysis/coverage-score');
|
|
7821
|
+
const { loadConfig: lc } = requireSourceOrBundled('./src/config/loader');
|
|
7822
|
+
const cfg = lc(cwd);
|
|
7823
|
+
// Use all files from srcDirs as proxies for "included" (no budget applied in health mode)
|
|
7824
|
+
const allFiles = buildFileList(cwd, cfg);
|
|
7825
|
+
const fakeEntries = allFiles.map(f => ({ filePath: f }));
|
|
7826
|
+
coverageResult = coverageScore(cwd, fakeEntries, cfg);
|
|
7827
|
+
} catch (_) {}
|
|
7728
7828
|
if (args.includes('--json')) {
|
|
7729
7829
|
// Feature 3 (VS Code) + Feature 5 (JetBrains): emit tokens + reduction for plugins
|
|
7730
7830
|
const ctxPath = path.join(cwd, '.github', 'copilot-instructions.md');
|
|
@@ -7733,10 +7833,21 @@ function main() {
|
|
|
7733
7833
|
if (fs.existsSync(ctxPath)) {
|
|
7734
7834
|
try { tokens = estimateTokens(fs.readFileSync(ctxPath, 'utf8')); } catch (_) {}
|
|
7735
7835
|
}
|
|
7736
|
-
|
|
7836
|
+
const payload = { ...result, tokens, reduction };
|
|
7837
|
+
if (coverageResult) {
|
|
7838
|
+
payload.coverage = coverageResult.score;
|
|
7839
|
+
payload.coverageGrade = coverageResult.grade;
|
|
7840
|
+
payload.coverageConfidence = coverageResult.confidence;
|
|
7841
|
+
payload.coverageTotalFiles = coverageResult.total;
|
|
7842
|
+
payload.coverageIncludedFiles = coverageResult.included;
|
|
7843
|
+
}
|
|
7844
|
+
process.stdout.write(JSON.stringify(payload) + '\n');
|
|
7737
7845
|
} else {
|
|
7738
7846
|
console.log('[sigmap] health:');
|
|
7739
7847
|
console.log(` score : ${result.score}/100 (grade ${result.grade})`);
|
|
7848
|
+
if (coverageResult) {
|
|
7849
|
+
console.log(` coverage : ${coverageResult.grade} (${coverageResult.score}%) — ${coverageResult.included} of ${coverageResult.total} source files`);
|
|
7850
|
+
}
|
|
7740
7851
|
console.log(` strategy : ${result.strategy}`);
|
|
7741
7852
|
console.log(` token reduction : ${result.tokenReductionPct !== null ? result.tokenReductionPct + '%' : 'no history'}`);
|
|
7742
7853
|
console.log(` days since regen: ${result.daysSinceRegen !== null ? result.daysSinceRegen : 'context file not found'}`);
|
package/package.json
CHANGED
|
@@ -26,15 +26,26 @@ const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js
|
|
|
26
26
|
*/
|
|
27
27
|
function format(context, opts = {}) {
|
|
28
28
|
if (!context || typeof context !== 'string') return '';
|
|
29
|
-
const version
|
|
29
|
+
const version = opts.version || 'unknown';
|
|
30
30
|
const timestamp = new Date().toISOString();
|
|
31
|
+
const meta = _confidenceMeta(opts);
|
|
31
32
|
return [
|
|
32
33
|
`<!-- Generated by SigMap v${version} — ${timestamp} -->`,
|
|
34
|
+
meta,
|
|
33
35
|
'',
|
|
34
36
|
context,
|
|
35
37
|
].join('\n');
|
|
36
38
|
}
|
|
37
39
|
|
|
40
|
+
function _confidenceMeta(opts) {
|
|
41
|
+
const parts = [`version=${opts.version || 'unknown'}`];
|
|
42
|
+
if (opts.confidence) parts.push(`confidence=${opts.confidence}`);
|
|
43
|
+
if (opts.coverage != null) parts.push(`coverage=${opts.coverage}%`);
|
|
44
|
+
if (opts.dropped != null) parts.push(`dropped=${opts.dropped}`);
|
|
45
|
+
if (opts.commit) parts.push(`commit=${opts.commit}`);
|
|
46
|
+
return `<!-- sigmap: ${parts.join(' ')} -->`;
|
|
47
|
+
}
|
|
48
|
+
|
|
38
49
|
/**
|
|
39
50
|
* Return the output file path for this adapter.
|
|
40
51
|
* @param {string} cwd - Project root
|
|
@@ -24,11 +24,13 @@ const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js
|
|
|
24
24
|
*/
|
|
25
25
|
function format(context, opts = {}) {
|
|
26
26
|
if (!context || typeof context !== 'string') return '';
|
|
27
|
-
const version
|
|
27
|
+
const version = opts.version || 'unknown';
|
|
28
28
|
const timestamp = new Date().toISOString();
|
|
29
|
+
const meta = _confidenceMeta(opts);
|
|
29
30
|
const header = [
|
|
30
31
|
`<!-- Generated by SigMap gen-context.js v${version} -->`,
|
|
31
32
|
`<!-- Updated: ${timestamp} -->`,
|
|
33
|
+
meta,
|
|
32
34
|
`<!-- Do not edit below — regenerate with: node gen-context.js -->`,
|
|
33
35
|
'',
|
|
34
36
|
'# Code signatures',
|
|
@@ -37,6 +39,15 @@ function format(context, opts = {}) {
|
|
|
37
39
|
return header + context;
|
|
38
40
|
}
|
|
39
41
|
|
|
42
|
+
function _confidenceMeta(opts) {
|
|
43
|
+
const parts = [`version=${opts.version || 'unknown'}`];
|
|
44
|
+
if (opts.confidence) parts.push(`confidence=${opts.confidence}`);
|
|
45
|
+
if (opts.coverage != null) parts.push(`coverage=${opts.coverage}%`);
|
|
46
|
+
if (opts.dropped != null) parts.push(`dropped=${opts.dropped}`);
|
|
47
|
+
if (opts.commit) parts.push(`commit=${opts.commit}`);
|
|
48
|
+
return `<!-- sigmap: ${parts.join(' ')} -->`;
|
|
49
|
+
}
|
|
50
|
+
|
|
40
51
|
/**
|
|
41
52
|
* Return the output file path for this adapter.
|
|
42
53
|
* @param {string} cwd - Project root
|
|
@@ -22,17 +22,28 @@ const name = 'cursor';
|
|
|
22
22
|
*/
|
|
23
23
|
function format(context, opts = {}) {
|
|
24
24
|
if (!context || typeof context !== 'string') return '';
|
|
25
|
-
const version
|
|
25
|
+
const version = opts.version || 'unknown';
|
|
26
26
|
const timestamp = new Date().toISOString();
|
|
27
|
+
const meta = _confidenceMeta(opts);
|
|
27
28
|
const header = [
|
|
28
29
|
`# Code signatures — generated by SigMap v${version}`,
|
|
29
30
|
`# Updated: ${timestamp}`,
|
|
31
|
+
`# ${meta}`,
|
|
30
32
|
`# Regenerate: node gen-context.js`,
|
|
31
33
|
'',
|
|
32
34
|
].join('\n');
|
|
33
35
|
return header + context;
|
|
34
36
|
}
|
|
35
37
|
|
|
38
|
+
function _confidenceMeta(opts) {
|
|
39
|
+
const parts = [`version=${opts.version || 'unknown'}`];
|
|
40
|
+
if (opts.confidence) parts.push(`confidence=${opts.confidence}`);
|
|
41
|
+
if (opts.coverage != null) parts.push(`coverage=${opts.coverage}%`);
|
|
42
|
+
if (opts.dropped != null) parts.push(`dropped=${opts.dropped}`);
|
|
43
|
+
if (opts.commit) parts.push(`commit=${opts.commit}`);
|
|
44
|
+
return `sigmap: ${parts.join(' ')}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
36
47
|
/**
|
|
37
48
|
* Return the output file path for this adapter.
|
|
38
49
|
* @param {string} cwd - Project root
|
|
@@ -36,9 +36,11 @@ function format(context, opts = {}) {
|
|
|
36
36
|
? `Project: ${opts.projectName}\n`
|
|
37
37
|
: '';
|
|
38
38
|
|
|
39
|
+
const meta = _confidenceMeta(opts);
|
|
39
40
|
return [
|
|
40
41
|
`You are a coding assistant with complete knowledge of this codebase.`,
|
|
41
42
|
`The following code signatures were extracted by SigMap v${version} on ${timestamp}.`,
|
|
43
|
+
`<!-- ${meta} -->`,
|
|
42
44
|
projectLine,
|
|
43
45
|
`These signatures represent every public function, class, and type in the project.`,
|
|
44
46
|
`Refer to them when answering questions about code structure, APIs, and implementation.`,
|
|
@@ -91,4 +93,13 @@ function write(context, cwd, opts = {}) {
|
|
|
91
93
|
fs.writeFileSync(filePath, newContent, 'utf8');
|
|
92
94
|
}
|
|
93
95
|
|
|
96
|
+
function _confidenceMeta(opts) {
|
|
97
|
+
const parts = [`version=${opts.version || 'unknown'}`];
|
|
98
|
+
if (opts.confidence) parts.push(`confidence=${opts.confidence}`);
|
|
99
|
+
if (opts.coverage != null) parts.push(`coverage=${opts.coverage}%`);
|
|
100
|
+
if (opts.dropped != null) parts.push(`dropped=${opts.dropped}`);
|
|
101
|
+
if (opts.commit) parts.push(`commit=${opts.commit}`);
|
|
102
|
+
return `sigmap: ${parts.join(' ')}`;
|
|
103
|
+
}
|
|
104
|
+
|
|
94
105
|
module.exports = { name, format, outputPath, write };
|
|
@@ -34,9 +34,11 @@ function format(context, opts = {}) {
|
|
|
34
34
|
? `Project: ${opts.projectName}\n`
|
|
35
35
|
: '';
|
|
36
36
|
|
|
37
|
+
const meta = _confidenceMeta(opts);
|
|
37
38
|
return [
|
|
38
39
|
`You are a coding assistant with full knowledge of this codebase.`,
|
|
39
40
|
`Below are the code signatures extracted by SigMap v${version} on ${timestamp}.`,
|
|
41
|
+
`<!-- ${meta} -->`,
|
|
40
42
|
projectLine,
|
|
41
43
|
`Use these signatures to answer questions about the code accurately.`,
|
|
42
44
|
`When the user asks about a specific file or function, refer to the signatures below.`,
|
|
@@ -57,4 +59,13 @@ function outputPath(cwd) {
|
|
|
57
59
|
return path.join(cwd, '.github', 'openai-context.md');
|
|
58
60
|
}
|
|
59
61
|
|
|
62
|
+
function _confidenceMeta(opts) {
|
|
63
|
+
const parts = [`version=${opts.version || 'unknown'}`];
|
|
64
|
+
if (opts.confidence) parts.push(`confidence=${opts.confidence}`);
|
|
65
|
+
if (opts.coverage != null) parts.push(`coverage=${opts.coverage}%`);
|
|
66
|
+
if (opts.dropped != null) parts.push(`dropped=${opts.dropped}`);
|
|
67
|
+
if (opts.commit) parts.push(`commit=${opts.commit}`);
|
|
68
|
+
return `sigmap: ${parts.join(' ')}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
60
71
|
module.exports = { name, format, outputPath };
|
|
@@ -22,17 +22,28 @@ const name = 'windsurf';
|
|
|
22
22
|
*/
|
|
23
23
|
function format(context, opts = {}) {
|
|
24
24
|
if (!context || typeof context !== 'string') return '';
|
|
25
|
-
const version
|
|
25
|
+
const version = opts.version || 'unknown';
|
|
26
26
|
const timestamp = new Date().toISOString();
|
|
27
|
+
const meta = _confidenceMeta(opts);
|
|
27
28
|
const header = [
|
|
28
29
|
`# Code signatures — generated by SigMap v${version}`,
|
|
29
30
|
`# Updated: ${timestamp}`,
|
|
31
|
+
`# ${meta}`,
|
|
30
32
|
`# Regenerate: node gen-context.js`,
|
|
31
33
|
'',
|
|
32
34
|
].join('\n');
|
|
33
35
|
return header + context;
|
|
34
36
|
}
|
|
35
37
|
|
|
38
|
+
function _confidenceMeta(opts) {
|
|
39
|
+
const parts = [`version=${opts.version || 'unknown'}`];
|
|
40
|
+
if (opts.confidence) parts.push(`confidence=${opts.confidence}`);
|
|
41
|
+
if (opts.coverage != null) parts.push(`coverage=${opts.coverage}%`);
|
|
42
|
+
if (opts.dropped != null) parts.push(`dropped=${opts.dropped}`);
|
|
43
|
+
if (opts.commit) parts.push(`commit=${opts.commit}`);
|
|
44
|
+
return `sigmap: ${parts.join(' ')}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
36
47
|
/**
|
|
37
48
|
* Return the output file path for this adapter.
|
|
38
49
|
* @param {string} cwd - Project root
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SigMap coverage scorer — v4.0.0
|
|
5
|
+
*
|
|
6
|
+
* Measures what fraction of source files made it into the context output
|
|
7
|
+
* after token-budget application. This is complementary to the health score:
|
|
8
|
+
* - Health score = context freshness / reduction quality / budget compliance
|
|
9
|
+
* - Coverage score = how much of the codebase is represented in context
|
|
10
|
+
*
|
|
11
|
+
* Grade scale: A ≥ 90% | B ≥ 75% | C ≥ 50% | D < 50%
|
|
12
|
+
*
|
|
13
|
+
* @param {string} cwd
|
|
14
|
+
* @param {Array<{filePath:string}>} fileEntries - files that made it into output
|
|
15
|
+
* @param {{srcDirs:string[], exclude:string[]}} config
|
|
16
|
+
* @returns {{
|
|
17
|
+
* score: number,
|
|
18
|
+
* grade: 'A'|'B'|'C'|'D',
|
|
19
|
+
* total: number,
|
|
20
|
+
* included: number,
|
|
21
|
+
* dropped: number,
|
|
22
|
+
* confidence: 'HIGH'|'MEDIUM'|'LOW',
|
|
23
|
+
* perModule: Map<string, {total:number, included:number, pct:number}>,
|
|
24
|
+
* }}
|
|
25
|
+
*/
|
|
26
|
+
function coverageScore(cwd, fileEntries, config) {
|
|
27
|
+
const fs = require('fs');
|
|
28
|
+
const path = require('path');
|
|
29
|
+
|
|
30
|
+
const srcDirs = (config && Array.isArray(config.srcDirs) && config.srcDirs.length > 0)
|
|
31
|
+
? config.srcDirs
|
|
32
|
+
: ['src', 'app', 'lib'];
|
|
33
|
+
|
|
34
|
+
const excludeSet = new Set([
|
|
35
|
+
'node_modules', '.git', 'dist', 'build', 'out', '__pycache__',
|
|
36
|
+
'.next', 'coverage', 'target', 'vendor', '.context',
|
|
37
|
+
]);
|
|
38
|
+
if (config && Array.isArray(config.exclude)) {
|
|
39
|
+
for (const x of config.exclude) excludeSet.add(String(x));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const includedSet = new Set((fileEntries || []).map(f => f.filePath));
|
|
43
|
+
|
|
44
|
+
// Walk all source files from srcDirs
|
|
45
|
+
const allSource = [];
|
|
46
|
+
for (const relDir of srcDirs) {
|
|
47
|
+
const absDir = path.resolve(cwd, relDir);
|
|
48
|
+
if (fs.existsSync(absDir)) _walk(absDir, excludeSet, allSource);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const total = allSource.length;
|
|
52
|
+
const included = allSource.filter(f => includedSet.has(f)).length;
|
|
53
|
+
const dropped = total - included;
|
|
54
|
+
const pct = total > 0 ? Math.round((included / total) * 100) : 100;
|
|
55
|
+
|
|
56
|
+
const grade = pct >= 90 ? 'A' : pct >= 75 ? 'B' : pct >= 50 ? 'C' : 'D';
|
|
57
|
+
const confidence = pct >= 90 ? 'HIGH' : pct >= 70 ? 'MEDIUM' : 'LOW';
|
|
58
|
+
|
|
59
|
+
// Per-module breakdown (one entry per srcDir)
|
|
60
|
+
const perModule = new Map();
|
|
61
|
+
for (const relDir of srcDirs) {
|
|
62
|
+
const absDir = path.resolve(cwd, relDir);
|
|
63
|
+
const modFiles = allSource.filter(f => f.startsWith(absDir + path.sep) || f === absDir);
|
|
64
|
+
const modIncl = modFiles.filter(f => includedSet.has(f)).length;
|
|
65
|
+
const modPct = modFiles.length > 0 ? Math.round((modIncl / modFiles.length) * 100) : 100;
|
|
66
|
+
perModule.set(relDir, { total: modFiles.length, included: modIncl, pct: modPct });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return { score: pct, grade, total, included, dropped, confidence, perModule };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function _walk(dir, excludeSet, out) {
|
|
73
|
+
const fs = require('fs');
|
|
74
|
+
const path = require('path');
|
|
75
|
+
let entries;
|
|
76
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch (_) { return; }
|
|
77
|
+
for (const e of entries) {
|
|
78
|
+
if (excludeSet.has(e.name)) continue;
|
|
79
|
+
const full = path.join(dir, e.name);
|
|
80
|
+
if (e.isDirectory()) { _walk(full, excludeSet, out); }
|
|
81
|
+
else if (e.isFile()) { out.push(full); }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
module.exports = { coverageScore };
|
package/src/eval/analyzer.js
CHANGED
|
@@ -147,6 +147,9 @@ function analyzeFiles(files, cwd, opts) {
|
|
|
147
147
|
const tokens = tokenCount(sigs);
|
|
148
148
|
const covered = hasCoverage(filePath, cwd);
|
|
149
149
|
const isSlow = slow && elapsedMs > slowMs;
|
|
150
|
+
// v4.0: signal quality = sigs per line-of-code (higher = more informative to LLMs)
|
|
151
|
+
const linesOfCode = content.split('\n').length;
|
|
152
|
+
const signalQuality = linesOfCode > 0 ? parseFloat((sigs.length / linesOfCode).toFixed(4)) : 0;
|
|
150
153
|
|
|
151
154
|
stats.push({
|
|
152
155
|
file: rel,
|
|
@@ -154,6 +157,8 @@ function analyzeFiles(files, cwd, opts) {
|
|
|
154
157
|
sigs: sigs.length,
|
|
155
158
|
tokens,
|
|
156
159
|
covered,
|
|
160
|
+
linesOfCode,
|
|
161
|
+
signalQuality,
|
|
157
162
|
elapsedMs: slow ? elapsedMs : undefined,
|
|
158
163
|
slow: slow ? isSlow : undefined,
|
|
159
164
|
});
|
package/src/mcp/server.js
CHANGED