universal-ast-mapper 1.18.0 → 1.19.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 +11 -0
- package/README.md +2 -1
- package/dist/report.js +22 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,17 @@ since 1.0.0, guarantees a stable MCP tool / CLI surface across the 1.x line.
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [1.19.0] — 2026-06-09 · Dashboard: coupling + SDP
|
|
10
|
+
- The health dashboard (`ast-map report` / `get_codebase_report`) now surfaces the
|
|
11
|
+
v1.14–1.16 architecture metrics: a **Module coupling** card (per-directory instability
|
|
12
|
+
bars with Ca/Ce) and a **Layer violations** card (stable→volatile SDP inversions),
|
|
13
|
+
plus an **SDP violations** stat tile.
|
|
14
|
+
- SDP violations now factor into the health score (small capped penalty), so a codebase
|
|
15
|
+
that systematically depends "uphill" on the stability gradient scores lower.
|
|
16
|
+
- `ReportData` gains `layerViolations` and `modules`; purely additive.
|
|
17
|
+
- Tests: 4 new assertions (131 total) — report carries the new data and the HTML renders
|
|
18
|
+
both cards.
|
|
19
|
+
|
|
9
20
|
## [1.18.0] — 2026-06-09 · Vue & Svelte SFC support
|
|
10
21
|
- `.vue` and `.svelte` **single-file components** are now first-class inputs. The
|
|
11
22
|
`<script>` / `<script setup>` block is lifted out and parsed with the TS/JS extractor
|
package/README.md
CHANGED
|
@@ -368,7 +368,7 @@ Given a compiled JS/CSS file with an inline (`data:`) or external `sourceMapping
|
|
|
368
368
|
---
|
|
369
369
|
|
|
370
370
|
### `get_codebase_report`
|
|
371
|
-
A one-shot **codebase health summary**: file/symbol counts, language breakdown, a health **grade (A–F)** + score, complexity hotspots, god nodes, dead exports, and
|
|
371
|
+
A one-shot **codebase health summary**: file/symbol counts, language breakdown, a health **grade (A–F)** + score, complexity hotspots, god nodes, dead exports, circular dependencies, **module coupling** (per-directory instability), and **layer violations** (SDP). Rendered as a premium HTML dashboard by `ast-map report`.
|
|
372
372
|
|
|
373
373
|
```json
|
|
374
374
|
{ "grade": "B", "score": 82, "fileCount": 120, "symbolCount": 1400,
|
|
@@ -740,6 +740,7 @@ Not part of the public API: the internal `src/` module layout and the generated
|
|
|
740
740
|
|
|
741
741
|
| Version | What changed |
|
|
742
742
|
|---------|--------------|
|
|
743
|
+
| **1.19.0** | **Dashboard: coupling + SDP** — `ast-map report` / `get_codebase_report` now include **module coupling** (per-directory instability bars) and **layer violations** (stable→volatile, SDP) cards, plus an SDP stat; SDP inversions also factor into the health score. The v1.14–1.16 metrics are now visual. |
|
|
743
744
|
| **1.18.0** | **Vue & Svelte SFC support** — `.vue` and `.svelte` single-file components are now parsed: the `<script>` / `<script setup>` block is lifted out (TS or JS) and its symbols + imports extracted, with cross-file graph edges into plain modules. Blank-padding keeps every symbol's line numbers pointing at the original SFC. **14 languages**. |
|
|
744
745
|
| **1.17.0** | **MCP prompts** — the server now registers 5 parameterized **prompts** (`architecture_audit`, `safe_refactor`, `dead_code_cleanup`, `health_check`, `onboard_codebase`): named workflows a client can invoke from its prompt menu, each chaining the right tools. The Cookbook recipes, one call away. |
|
|
745
746
|
| **1.16.0** | **Module coupling** — new `get_module_coupling` MCP tool + `ast-map modules` (alias `mods`) CLI: aggregates the import graph to the **directory/module level** — per-module Ca / Ce / instability plus weighted inter-module edges (intra-module imports ignored). The bird's-eye view above per-file coupling. **27 MCP tools**. |
|
package/dist/report.js
CHANGED
|
@@ -4,6 +4,8 @@ import { resolveOptions } from "./config.js";
|
|
|
4
4
|
import { buildSymbolGraph } from "./graph.js";
|
|
5
5
|
import { findDeadExports, findCircularDeps, getTopSymbols } from "./graph-analysis.js";
|
|
6
6
|
import { computeFileComplexity } from "./complexity.js";
|
|
7
|
+
import { findLayerViolations } from "./layers.js";
|
|
8
|
+
import { computeModuleCoupling } from "./modulecoupling.js";
|
|
7
9
|
function gradeFor(score) {
|
|
8
10
|
if (score >= 90)
|
|
9
11
|
return "A";
|
|
@@ -46,6 +48,8 @@ export async function buildReport(absDir, root) {
|
|
|
46
48
|
const dead = findDeadExports(graph).filter((d) => d.confidence === "high");
|
|
47
49
|
const cycles = findCircularDeps(graph);
|
|
48
50
|
const god = getTopSymbols(graph, 8);
|
|
51
|
+
const layerViolations = findLayerViolations(graph);
|
|
52
|
+
const modules = computeModuleCoupling(graph).modules;
|
|
49
53
|
hotspots.sort((a, b) => b.complexity - a.complexity);
|
|
50
54
|
const veryHigh = hotspots.filter((f) => f.complexity > 20).length;
|
|
51
55
|
const high = hotspots.filter((f) => f.complexity > 10 && f.complexity <= 20).length;
|
|
@@ -55,6 +59,7 @@ export async function buildReport(absDir, root) {
|
|
|
55
59
|
score -= Math.min(22, cycles.length * 6);
|
|
56
60
|
score -= Math.min(28, veryHigh * 4 + high * 1);
|
|
57
61
|
score -= Math.min(12, god.filter((g) => g.importCount >= 8).length * 4);
|
|
62
|
+
score -= Math.min(10, layerViolations.length);
|
|
58
63
|
score = Math.max(0, Math.round(score));
|
|
59
64
|
const languages = [...langCount.entries()]
|
|
60
65
|
.map(([lang, f]) => ({ lang, files: f }))
|
|
@@ -72,6 +77,8 @@ export async function buildReport(absDir, root) {
|
|
|
72
77
|
cycles: { count: cycles.length, items: cycles.slice(0, 12).map((c) => c.cycle) },
|
|
73
78
|
godNodes: god.map((g) => ({ symbol: g.symbol, file: g.file, importCount: g.importCount })),
|
|
74
79
|
complexity: { average: cxN ? Math.round((cxSum / cxN) * 10) / 10 : 0, max: cxMax, hotspots: hotspots.slice(0, 12) },
|
|
80
|
+
layerViolations: { count: layerViolations.length, items: layerViolations.slice(0, 12) },
|
|
81
|
+
modules: modules.slice(0, 10),
|
|
75
82
|
};
|
|
76
83
|
}
|
|
77
84
|
/* ─── Premium HTML dashboard ───────────────────────────────────────────────── */
|
|
@@ -84,6 +91,9 @@ function esc(s) {
|
|
|
84
91
|
function ratingColor(r) {
|
|
85
92
|
return r === "very-high" ? "#e24b4a" : r === "high" ? "#d85a30" : r === "moderate" ? "#ba7517" : "#1d9e75";
|
|
86
93
|
}
|
|
94
|
+
function instColor(i) {
|
|
95
|
+
return i >= 0.8 ? "#e24b4a" : i <= 0.2 ? "#1d9e75" : "#ba7517";
|
|
96
|
+
}
|
|
87
97
|
function statCard(label, value, accent) {
|
|
88
98
|
return `<div class="stat"><div class="sv"${accent ? ` style="color:${accent}"` : ""}>${value}</div><div class="sl">${label}</div></div>`;
|
|
89
99
|
}
|
|
@@ -109,6 +119,13 @@ export function buildReportHtml(d) {
|
|
|
109
119
|
const cycles = d.cycles.count
|
|
110
120
|
? d.cycles.items.map((c) => `<div class="li"><span class="mono">${esc(c.join(" → "))}</span></div>`).join("")
|
|
111
121
|
: `<div class="ok">✓ No circular dependencies</div>`;
|
|
122
|
+
const modules = d.modules.length
|
|
123
|
+
? d.modules.map((m) => bar(`${m.module} · ${m.files} file(s)`, m.instability, 1, instColor(m.instability), `Ca ${m.afferent} · Ce ${m.efferent} · <b>I ${m.instability.toFixed(2)}</b>`)).join("")
|
|
124
|
+
: `<div class="empty">No cross-module imports.</div>`;
|
|
125
|
+
const sdp = d.layerViolations.count
|
|
126
|
+
? d.layerViolations.items.map((v) => `<div class="li"><span class="mono">${esc(v.from)}</span><span class="dim">→ ${esc(v.to)}</span><span class="pill" style="color:${instColor(0.9)}">+${v.severity.toFixed(2)}</span></div>`).join("")
|
|
127
|
+
+ (d.layerViolations.count > d.layerViolations.items.length ? `<div class="more">+${d.layerViolations.count - d.layerViolations.items.length} more…</div>` : "")
|
|
128
|
+
: `<div class="ok">✓ No stability inversions (SDP)</div>`;
|
|
112
129
|
return `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
113
130
|
<title>${esc(d.project)} — code health</title><style>
|
|
114
131
|
:root{--bg:#fafaf8;--card:#fff;--bd:#e7e5df;--tx:#2b2b28;--dim:#8a8880;--soft:#f1efe9}
|
|
@@ -149,6 +166,7 @@ export function buildReportHtml(d) {
|
|
|
149
166
|
${statCard("Max complexity", d.complexity.max, ratingColor(d.complexity.max > 20 ? "very-high" : d.complexity.max > 10 ? "high" : "low"))}
|
|
150
167
|
${statCard("Dead exports", d.dead.count, d.dead.count ? "#d85a30" : "#1d9e75")}
|
|
151
168
|
${statCard("Cycles", d.cycles.count, d.cycles.count ? "#e24b4a" : "#1d9e75")}
|
|
169
|
+
${statCard("SDP violations", d.layerViolations.count, d.layerViolations.count ? "#d85a30" : "#1d9e75")}
|
|
152
170
|
</div>
|
|
153
171
|
<div class="card"><h2>Language breakdown</h2>${langs}</div>
|
|
154
172
|
<div class="card"><h2>Complexity hotspots</h2>${hotspots}</div>
|
|
@@ -156,6 +174,10 @@ export function buildReportHtml(d) {
|
|
|
156
174
|
<div class="card"><h2>God nodes (most imported)</h2>${god}</div>
|
|
157
175
|
<div class="card"><h2>Circular dependencies</h2>${cycles}</div>
|
|
158
176
|
</div>
|
|
177
|
+
<div class="two">
|
|
178
|
+
<div class="card"><h2>Module coupling (instability)</h2>${modules}</div>
|
|
179
|
+
<div class="card"><h2>Layer violations (stable → volatile)</h2>${sdp}</div>
|
|
180
|
+
</div>
|
|
159
181
|
<div class="card"><h2>Dead exports (high confidence)</h2>${dead}</div>
|
|
160
182
|
<div class="foot">Generated by AST-MCP · universal-ast-mapper</div>
|
|
161
183
|
</div></body></html>`;
|
package/package.json
CHANGED