sigmap 7.13.0 → 7.15.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/CHANGELOG.md +18 -0
- package/gen-context.js +193 -2
- package/llms-full.txt +1 -1
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/core/package.json +1 -1
- package/src/conventions/ci.js +48 -0
- package/src/conventions/report.js +75 -0
- package/src/mcp/server.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,24 @@ Format: [Semantic Versioning](https://semver.org/)
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
+
## [7.15.0] — 2026-06-18
|
|
14
|
+
|
|
15
|
+
Minor release — `sigmap conventions --ci` (grounded codegen, Layer 3 polish).
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- **`sigmap conventions --ci` — gate CI on convention consistency (#322):** completes the consistency-tracking story started by `--report` (v7.14.0). A CI gate that fails when a repo's overall convention consistency falls below a threshold (`--min`, default 0.70), and — with `--no-regress` — also fails when the score dropped vs the last recorded snapshot (best-effort). New zero-dependency, bundle-safe `src/conventions/ci.js` (`ciGate`) reuses `overallScore`; the command is read-only (reads the last `.context/conventions-history.ndjson` snapshot for `--no-regress`, never appends) and exits non-zero on failure, so it drops straight into CI. `--json` for machine output. The remaining `conventions` flags (`--fix`, `--update`) and the §9 LLM A/B benchmark are follow-ups.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## [7.14.0] — 2026-06-17
|
|
23
|
+
|
|
24
|
+
Minor release — `sigmap conventions --report` (grounded codegen, Layer 3 polish).
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- **`sigmap conventions --report` — consistency audit + trend vs last run (#319):** the next `conventions` flag (IMPL.md §4). Reports a per-convention consistency score (file naming, export style) and a single file-count-weighted **overall consistency score**, each with a delta vs the previous run — a trackable "how consistent is our style, and is it improving?" number. New zero-dependency, bundle-safe `src/conventions/report.js` (`scoreReport`, `snapshot`, `overallScore`); the command compares against the last snapshot in `.context/conventions-history.ndjson`, prints the audit with ▲/▼ trend arrows, and appends a fresh snapshot. `--json` for machine output. The remaining `conventions` flags (`--fix`, `--update`, `--ci`) and the §9 LLM A/B benchmark are follow-ups.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
13
31
|
## [7.13.0] — 2026-06-17
|
|
14
32
|
|
|
15
33
|
Minor release — `sigmap create` (grounded codegen, Gap 2 — the pipeline capstone).
|
package/gen-context.js
CHANGED
|
@@ -29,6 +29,137 @@ function __require(key) {
|
|
|
29
29
|
// ── ./src/cache/freshen ──
|
|
30
30
|
// ── ./src/review/review-pr ──
|
|
31
31
|
// ── ./src/create/orchestrate ──
|
|
32
|
+
// ── ./src/conventions/report ──
|
|
33
|
+
// ── ./src/conventions/ci ──
|
|
34
|
+
__factories["./src/conventions/ci"] = function(module, exports) {
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Convention CI gate (IMPL.md §4 — `conventions --ci`).
|
|
38
|
+
*
|
|
39
|
+
* Fails CI when a repo's overall convention consistency is below a threshold,
|
|
40
|
+
* and optionally when it regresses vs the last recorded run. Builds on the
|
|
41
|
+
* `--report` score. Pure, zero-dependency, bundle-safe.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
const { overallScore } = __require('./src/conventions/report');
|
|
45
|
+
|
|
46
|
+
const DEFAULT_MIN = 0.7;
|
|
47
|
+
const EPS = 1e-9;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Evaluate the consistency gate.
|
|
51
|
+
* @param {object} result an `extractConventions` result
|
|
52
|
+
* @param {object} [opts]
|
|
53
|
+
* @param {number} [opts.min=0.7] minimum overall consistency (0–1)
|
|
54
|
+
* @param {boolean} [opts.noRegress=false] also fail if the score dropped vs prior
|
|
55
|
+
* @param {object|null} [prior] the previous snapshot (from `report.snapshot`)
|
|
56
|
+
* @returns {{ score:number, min:number, ok:boolean, regressed:boolean, reasons:string[] }}
|
|
57
|
+
*/
|
|
58
|
+
function ciGate(result, opts = {}, prior = null) {
|
|
59
|
+
const min = opts.min != null ? opts.min : DEFAULT_MIN;
|
|
60
|
+
const score = overallScore(result);
|
|
61
|
+
const reasons = [];
|
|
62
|
+
let ok = true;
|
|
63
|
+
|
|
64
|
+
if (score < min) {
|
|
65
|
+
ok = false;
|
|
66
|
+
reasons.push(`consistency ${(score * 100).toFixed(0)}% below min ${(min * 100).toFixed(0)}%`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let regressed = false;
|
|
70
|
+
if (opts.noRegress && prior && typeof prior.score === 'number') {
|
|
71
|
+
if (score < prior.score - EPS) {
|
|
72
|
+
regressed = true;
|
|
73
|
+
ok = false;
|
|
74
|
+
reasons.push(`consistency dropped ${(prior.score * 100).toFixed(0)}% → ${(score * 100).toFixed(0)}%`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return { score, min, ok, regressed, reasons };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = { ciGate, DEFAULT_MIN };
|
|
82
|
+
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
__factories["./src/conventions/report"] = function(module, exports) {
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Convention audit + trend (IMPL.md §4 — `conventions --report`).
|
|
89
|
+
*
|
|
90
|
+
* Turns an `extractConventions` result into an audit: a consistency score per
|
|
91
|
+
* convention plus a single file-count-weighted overall score, each with a delta
|
|
92
|
+
* vs the previous run (the trend). Pure, zero-dependency, bundle-safe.
|
|
93
|
+
*/
|
|
94
|
+
|
|
95
|
+
const NAMES = { fileNaming: 'file naming', exportStyle: 'export style' };
|
|
96
|
+
|
|
97
|
+
/** File-count-weighted mean of the scored conventions' dominant shares (0–1). */
|
|
98
|
+
function overallScore(result) {
|
|
99
|
+
let num = 0;
|
|
100
|
+
let den = 0;
|
|
101
|
+
for (const key of ['fileNaming', 'exportStyle']) {
|
|
102
|
+
const c = result && result[key];
|
|
103
|
+
if (c && c.total > 0) { num += c.dominantPct * c.total; den += c.total; }
|
|
104
|
+
}
|
|
105
|
+
return den > 0 ? num / den : 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Build a consistency report with trend vs a prior snapshot.
|
|
110
|
+
* @param {object} result an `extractConventions` result
|
|
111
|
+
* @param {object|null} [prior] a previous snapshot (this module's `snapshot` shape)
|
|
112
|
+
* @returns {{ conventions: object[], testFramework: string|null,
|
|
113
|
+
* score: number, prevScore: number|null, scoreDelta: number|null }}
|
|
114
|
+
*/
|
|
115
|
+
function scoreReport(result, prior) {
|
|
116
|
+
const conventions = [];
|
|
117
|
+
for (const key of ['fileNaming', 'exportStyle']) {
|
|
118
|
+
const c = (result && result[key]) || { dominant: null, dominantPct: 0, total: 0, tier: 'unknown' };
|
|
119
|
+
const priorPct = prior && prior[key] && typeof prior[key].dominantPct === 'number' ? prior[key].dominantPct : null;
|
|
120
|
+
conventions.push({
|
|
121
|
+
key,
|
|
122
|
+
name: NAMES[key] || key,
|
|
123
|
+
dominant: c.dominant,
|
|
124
|
+
dominantPct: c.dominantPct,
|
|
125
|
+
tier: c.tier,
|
|
126
|
+
total: c.total,
|
|
127
|
+
delta: priorPct == null ? null : c.dominantPct - priorPct,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
const score = overallScore(result);
|
|
131
|
+
const prevScore = prior && typeof prior.score === 'number' ? prior.score : null;
|
|
132
|
+
return {
|
|
133
|
+
conventions,
|
|
134
|
+
testFramework: (result && result.testFramework) || null,
|
|
135
|
+
score,
|
|
136
|
+
prevScore,
|
|
137
|
+
scoreDelta: prevScore == null ? null : score - prevScore,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* A compact, persistable snapshot of a run (one line in the history log).
|
|
143
|
+
* @param {object} result an `extractConventions` result
|
|
144
|
+
* @param {string} [ts] ISO timestamp (caller supplies — keeps this pure)
|
|
145
|
+
*/
|
|
146
|
+
function snapshot(result, ts) {
|
|
147
|
+
const pick = (c) => (c && c.total > 0
|
|
148
|
+
? { dominant: c.dominant, dominantPct: c.dominantPct, tier: c.tier, total: c.total }
|
|
149
|
+
: { dominant: null, dominantPct: 0, tier: 'unknown', total: 0 });
|
|
150
|
+
return {
|
|
151
|
+
ts: ts || null,
|
|
152
|
+
fileNaming: pick(result && result.fileNaming),
|
|
153
|
+
exportStyle: pick(result && result.exportStyle),
|
|
154
|
+
testFramework: (result && result.testFramework) || null,
|
|
155
|
+
score: overallScore(result),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
module.exports = { scoreReport, snapshot, overallScore };
|
|
160
|
+
|
|
161
|
+
};
|
|
162
|
+
|
|
32
163
|
__factories["./src/create/orchestrate"] = function(module, exports) {
|
|
33
164
|
|
|
34
165
|
/**
|
|
@@ -7459,7 +7590,7 @@ __factories["./src/mcp/server"] = function(module, exports) {
|
|
|
7459
7590
|
|
|
7460
7591
|
const SERVER_INFO = {
|
|
7461
7592
|
name: 'sigmap',
|
|
7462
|
-
version: '7.
|
|
7593
|
+
version: '7.15.0',
|
|
7463
7594
|
description: 'SigMap MCP server — code signatures on demand',
|
|
7464
7595
|
};
|
|
7465
7596
|
|
|
@@ -13137,7 +13268,7 @@ function __tryGit(args, opts = {}) {
|
|
|
13137
13268
|
catch (_) { return ''; }
|
|
13138
13269
|
}
|
|
13139
13270
|
|
|
13140
|
-
const VERSION = '7.
|
|
13271
|
+
const VERSION = '7.15.0';
|
|
13141
13272
|
const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
|
|
13142
13273
|
|
|
13143
13274
|
function requireSourceOrBundled(key) {
|
|
@@ -16332,6 +16463,66 @@ function main() {
|
|
|
16332
16463
|
process.exit(0);
|
|
16333
16464
|
}
|
|
16334
16465
|
|
|
16466
|
+
// `--ci`: gate — fail when overall consistency is below a threshold (or regresses).
|
|
16467
|
+
if (args.includes('--ci')) {
|
|
16468
|
+
const { ciGate } = requireSourceOrBundled('./src/conventions/ci');
|
|
16469
|
+
const minIdx = args.indexOf('--min');
|
|
16470
|
+
const min = minIdx !== -1 && args[minIdx + 1] ? parseFloat(args[minIdx + 1]) : undefined;
|
|
16471
|
+
const noRegress = args.includes('--no-regress');
|
|
16472
|
+
let prior = null;
|
|
16473
|
+
if (noRegress) {
|
|
16474
|
+
try {
|
|
16475
|
+
const lines = fs.readFileSync(path.join(cwd, '.context', 'conventions-history.ndjson'), 'utf8').split('\n').filter(Boolean);
|
|
16476
|
+
if (lines.length) prior = JSON.parse(lines[lines.length - 1]);
|
|
16477
|
+
} catch (_) {}
|
|
16478
|
+
}
|
|
16479
|
+
const gate = ciGate(result, { min, noRegress }, prior);
|
|
16480
|
+
if (jsonOut) {
|
|
16481
|
+
process.stdout.write(JSON.stringify(gate) + '\n');
|
|
16482
|
+
process.exit(gate.ok ? 0 : 1);
|
|
16483
|
+
}
|
|
16484
|
+
const pctC = (n) => `${(n * 100).toFixed(0)}%`;
|
|
16485
|
+
if (gate.ok) {
|
|
16486
|
+
console.log(`[sigmap] conventions --ci ✓ PASS — consistency ${pctC(gate.score)} (min ${pctC(gate.min)})`);
|
|
16487
|
+
process.exit(0);
|
|
16488
|
+
}
|
|
16489
|
+
console.log(`[sigmap] conventions --ci ✗ FAIL — consistency ${pctC(gate.score)} (min ${pctC(gate.min)})`);
|
|
16490
|
+
for (const r of gate.reasons) console.log(` • ${r}`);
|
|
16491
|
+
process.exit(1);
|
|
16492
|
+
}
|
|
16493
|
+
|
|
16494
|
+
// `--report`: consistency audit + score + trend vs the last run.
|
|
16495
|
+
if (args.includes('--report')) {
|
|
16496
|
+
const { scoreReport, snapshot } = requireSourceOrBundled('./src/conventions/report');
|
|
16497
|
+
const histPath = path.join(cwd, '.context', 'conventions-history.ndjson');
|
|
16498
|
+
let prior = null;
|
|
16499
|
+
try {
|
|
16500
|
+
const lines = fs.readFileSync(histPath, 'utf8').split('\n').filter(Boolean);
|
|
16501
|
+
if (lines.length) prior = JSON.parse(lines[lines.length - 1]);
|
|
16502
|
+
} catch (_) {}
|
|
16503
|
+
const report = scoreReport(result, prior);
|
|
16504
|
+
// Append the new snapshot to the history log.
|
|
16505
|
+
try {
|
|
16506
|
+
fs.mkdirSync(path.join(cwd, '.context'), { recursive: true });
|
|
16507
|
+
fs.appendFileSync(histPath, JSON.stringify(snapshot(result, new Date().toISOString())) + '\n');
|
|
16508
|
+
} catch (_) {}
|
|
16509
|
+
|
|
16510
|
+
if (jsonOut) {
|
|
16511
|
+
process.stdout.write(JSON.stringify(report) + '\n');
|
|
16512
|
+
process.exit(0);
|
|
16513
|
+
}
|
|
16514
|
+
const pctR = (n) => `${(n * 100).toFixed(0)}%`;
|
|
16515
|
+
const arrow = (d) => (d == null ? '' : d > 0.0005 ? ` ▲${(d * 100).toFixed(0)}pp` : d < -0.0005 ? ` ▼${(Math.abs(d) * 100).toFixed(0)}pp` : ' =');
|
|
16516
|
+
console.log('[sigmap] conventions --report (TS/JS/Python)');
|
|
16517
|
+
console.log(` overall consistency: ${pctR(report.score)}${arrow(report.scoreDelta)}${report.prevScore == null ? ' (first run)' : ''}`);
|
|
16518
|
+
for (const c of report.conventions) {
|
|
16519
|
+
if (c.total === 0) { console.log(` ${c.name.padEnd(14)} n/a (no samples)`); continue; }
|
|
16520
|
+
console.log(` ${c.name.padEnd(14)} ${c.dominant} ${pctR(c.dominantPct)} [${c.tier}]${arrow(c.delta)}`);
|
|
16521
|
+
}
|
|
16522
|
+
console.log(` ${'test framework'.padEnd(14)} ${report.testFramework || 'none detected'}`);
|
|
16523
|
+
process.exit(0);
|
|
16524
|
+
}
|
|
16525
|
+
|
|
16335
16526
|
// `--conflicts`: surface why a convention is mixed (breakdown + rename suggestions).
|
|
16336
16527
|
if (args.includes('--conflicts')) {
|
|
16337
16528
|
const { analyzeConflicts } = requireSourceOrBundled('./src/conventions/conflicts');
|
package/llms-full.txt
CHANGED
|
@@ -9,7 +9,7 @@ the files relevant to the task — cutting tokens ~97% while keeping answers
|
|
|
9
9
|
grounded. Deterministic, offline, no embeddings or vector database. Works with
|
|
10
10
|
Claude, Cursor, GitHub Copilot, Aider, Windsurf, local LLMs, and MCP.
|
|
11
11
|
|
|
12
|
-
# Version: 7.
|
|
12
|
+
# Version: 7.15.0 | Benchmark: sigmap-v7.0-main (2026-06-14)
|
|
13
13
|
# Source: auto-generated from package.json, version.json, src/mcp/tools.js, src/config/defaults.js
|
|
14
14
|
# Regenerate: npm run generate:llms | Validate: npm run validate:llms
|
|
15
15
|
|
package/llms.txt
CHANGED
|
@@ -9,7 +9,7 @@ the files relevant to the task — cutting tokens ~97% while keeping answers
|
|
|
9
9
|
grounded. Deterministic, offline, no embeddings or vector database. Works with
|
|
10
10
|
Claude, Cursor, GitHub Copilot, Aider, Windsurf, local LLMs, and MCP.
|
|
11
11
|
|
|
12
|
-
# Version: 7.
|
|
12
|
+
# Version: 7.15.0 | Benchmark: sigmap-v7.0-main (2026-06-14)
|
|
13
13
|
# Source: auto-generated from package.json, version.json, src/mcp/tools.js, src/config/defaults.js
|
|
14
14
|
# Regenerate: npm run generate:llms | Validate: npm run validate:llms
|
|
15
15
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sigmap",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.15.0",
|
|
4
4
|
"description": "97% token reduction for AI coding. Extracts function & class signatures with TF-IDF ranking to feed only the right files to Claude, Cursor, Copilot, Aider, Windsurf, local LLMs & MCP. Zero dependencies, runs offline via npx.",
|
|
5
5
|
"main": "packages/core/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Convention CI gate (IMPL.md §4 — `conventions --ci`).
|
|
5
|
+
*
|
|
6
|
+
* Fails CI when a repo's overall convention consistency is below a threshold,
|
|
7
|
+
* and optionally when it regresses vs the last recorded run. Builds on the
|
|
8
|
+
* `--report` score. Pure, zero-dependency, bundle-safe.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const { overallScore } = require('./report');
|
|
12
|
+
|
|
13
|
+
const DEFAULT_MIN = 0.7;
|
|
14
|
+
const EPS = 1e-9;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Evaluate the consistency gate.
|
|
18
|
+
* @param {object} result an `extractConventions` result
|
|
19
|
+
* @param {object} [opts]
|
|
20
|
+
* @param {number} [opts.min=0.7] minimum overall consistency (0–1)
|
|
21
|
+
* @param {boolean} [opts.noRegress=false] also fail if the score dropped vs prior
|
|
22
|
+
* @param {object|null} [prior] the previous snapshot (from `report.snapshot`)
|
|
23
|
+
* @returns {{ score:number, min:number, ok:boolean, regressed:boolean, reasons:string[] }}
|
|
24
|
+
*/
|
|
25
|
+
function ciGate(result, opts = {}, prior = null) {
|
|
26
|
+
const min = opts.min != null ? opts.min : DEFAULT_MIN;
|
|
27
|
+
const score = overallScore(result);
|
|
28
|
+
const reasons = [];
|
|
29
|
+
let ok = true;
|
|
30
|
+
|
|
31
|
+
if (score < min) {
|
|
32
|
+
ok = false;
|
|
33
|
+
reasons.push(`consistency ${(score * 100).toFixed(0)}% below min ${(min * 100).toFixed(0)}%`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let regressed = false;
|
|
37
|
+
if (opts.noRegress && prior && typeof prior.score === 'number') {
|
|
38
|
+
if (score < prior.score - EPS) {
|
|
39
|
+
regressed = true;
|
|
40
|
+
ok = false;
|
|
41
|
+
reasons.push(`consistency dropped ${(prior.score * 100).toFixed(0)}% → ${(score * 100).toFixed(0)}%`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return { score, min, ok, regressed, reasons };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = { ciGate, DEFAULT_MIN };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Convention audit + trend (IMPL.md §4 — `conventions --report`).
|
|
5
|
+
*
|
|
6
|
+
* Turns an `extractConventions` result into an audit: a consistency score per
|
|
7
|
+
* convention plus a single file-count-weighted overall score, each with a delta
|
|
8
|
+
* vs the previous run (the trend). Pure, zero-dependency, bundle-safe.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const NAMES = { fileNaming: 'file naming', exportStyle: 'export style' };
|
|
12
|
+
|
|
13
|
+
/** File-count-weighted mean of the scored conventions' dominant shares (0–1). */
|
|
14
|
+
function overallScore(result) {
|
|
15
|
+
let num = 0;
|
|
16
|
+
let den = 0;
|
|
17
|
+
for (const key of ['fileNaming', 'exportStyle']) {
|
|
18
|
+
const c = result && result[key];
|
|
19
|
+
if (c && c.total > 0) { num += c.dominantPct * c.total; den += c.total; }
|
|
20
|
+
}
|
|
21
|
+
return den > 0 ? num / den : 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Build a consistency report with trend vs a prior snapshot.
|
|
26
|
+
* @param {object} result an `extractConventions` result
|
|
27
|
+
* @param {object|null} [prior] a previous snapshot (this module's `snapshot` shape)
|
|
28
|
+
* @returns {{ conventions: object[], testFramework: string|null,
|
|
29
|
+
* score: number, prevScore: number|null, scoreDelta: number|null }}
|
|
30
|
+
*/
|
|
31
|
+
function scoreReport(result, prior) {
|
|
32
|
+
const conventions = [];
|
|
33
|
+
for (const key of ['fileNaming', 'exportStyle']) {
|
|
34
|
+
const c = (result && result[key]) || { dominant: null, dominantPct: 0, total: 0, tier: 'unknown' };
|
|
35
|
+
const priorPct = prior && prior[key] && typeof prior[key].dominantPct === 'number' ? prior[key].dominantPct : null;
|
|
36
|
+
conventions.push({
|
|
37
|
+
key,
|
|
38
|
+
name: NAMES[key] || key,
|
|
39
|
+
dominant: c.dominant,
|
|
40
|
+
dominantPct: c.dominantPct,
|
|
41
|
+
tier: c.tier,
|
|
42
|
+
total: c.total,
|
|
43
|
+
delta: priorPct == null ? null : c.dominantPct - priorPct,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const score = overallScore(result);
|
|
47
|
+
const prevScore = prior && typeof prior.score === 'number' ? prior.score : null;
|
|
48
|
+
return {
|
|
49
|
+
conventions,
|
|
50
|
+
testFramework: (result && result.testFramework) || null,
|
|
51
|
+
score,
|
|
52
|
+
prevScore,
|
|
53
|
+
scoreDelta: prevScore == null ? null : score - prevScore,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* A compact, persistable snapshot of a run (one line in the history log).
|
|
59
|
+
* @param {object} result an `extractConventions` result
|
|
60
|
+
* @param {string} [ts] ISO timestamp (caller supplies — keeps this pure)
|
|
61
|
+
*/
|
|
62
|
+
function snapshot(result, ts) {
|
|
63
|
+
const pick = (c) => (c && c.total > 0
|
|
64
|
+
? { dominant: c.dominant, dominantPct: c.dominantPct, tier: c.tier, total: c.total }
|
|
65
|
+
: { dominant: null, dominantPct: 0, tier: 'unknown', total: 0 });
|
|
66
|
+
return {
|
|
67
|
+
ts: ts || null,
|
|
68
|
+
fileNaming: pick(result && result.fileNaming),
|
|
69
|
+
exportStyle: pick(result && result.exportStyle),
|
|
70
|
+
testFramework: (result && result.testFramework) || null,
|
|
71
|
+
score: overallScore(result),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
module.exports = { scoreReport, snapshot, overallScore };
|
package/src/mcp/server.js
CHANGED