tech-debt-visualizer 0.2.1 → 0.2.3
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/README.md +2 -0
- package/dist/llm.js +9 -8
- package/dist/reports/assets/report.css +285 -128
- package/dist/reports/assets/report.js +98 -28
- package/dist/reports/html.js +66 -42
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -68,6 +68,8 @@ So: **static metrics + git → score & tier → optional LLM explanations and co
|
|
|
68
68
|
| **Global (after publish)** | `npm install -g tech-debt-visualizer` then `tech-debt analyze [path]` |
|
|
69
69
|
| **No install (after publish)** | `npx tech-debt-visualizer analyze [path]` |
|
|
70
70
|
|
|
71
|
+
Use **`tech-debt-visualizer`** in the command (not `tech-debt`); `npx tech-debt` runs a different npm package. From this repo you can also run `npm run analyze` or `node dist/cli.js analyze .`.
|
|
72
|
+
|
|
71
73
|
Requires **Node 18+**.
|
|
72
74
|
|
|
73
75
|
---
|
package/dist/llm.js
CHANGED
|
@@ -70,9 +70,10 @@ function parseFileAssessmentResponse(raw) {
|
|
|
70
70
|
}
|
|
71
71
|
/** Resolve provider and auth from config + env. Explicit baseURL = OpenAI-compatible; else key format or env picks provider. */
|
|
72
72
|
export function resolveLLMConfig(config = {}) {
|
|
73
|
+
const trim = (s) => (typeof s === "string" ? s.trim() : undefined) || undefined;
|
|
73
74
|
const explicitBase = (config.baseURL ?? process.env.OPENAI_BASE_URL)?.replace(/\/$/, "");
|
|
74
|
-
const cliKey = config.apiKey;
|
|
75
|
-
const openaiKey = cliKey ?? process.env.OPENAI_API_KEY ?? process.env.ANTHROPIC_API_KEY;
|
|
75
|
+
const cliKey = trim(config.apiKey);
|
|
76
|
+
const openaiKey = trim(cliKey ?? process.env.OPENAI_API_KEY ?? process.env.ANTHROPIC_API_KEY);
|
|
76
77
|
if (explicitBase && openaiKey) {
|
|
77
78
|
return {
|
|
78
79
|
provider: "openai",
|
|
@@ -99,20 +100,20 @@ export function resolveLLMConfig(config = {}) {
|
|
|
99
100
|
};
|
|
100
101
|
}
|
|
101
102
|
}
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
const openRouterKey = trim(cliKey ?? process.env.OPENROUTER_API_KEY);
|
|
104
|
+
if (openRouterKey) {
|
|
104
105
|
return {
|
|
105
106
|
provider: "openrouter",
|
|
106
|
-
apiKey:
|
|
107
|
+
apiKey: openRouterKey,
|
|
107
108
|
baseURL: config.baseURL ?? process.env.OPENROUTER_BASE_URL ?? OPENROUTER_BASE,
|
|
108
109
|
model: config.model ?? process.env.OPENROUTER_MODEL ?? OPENROUTER_DEFAULT_MODEL,
|
|
109
110
|
};
|
|
110
111
|
}
|
|
111
|
-
|
|
112
|
-
|
|
112
|
+
const geminiKey = trim(cliKey ?? process.env.GEMINI_API_KEY ?? process.env.GOOGLE_GENAI_API_KEY);
|
|
113
|
+
if (geminiKey) {
|
|
113
114
|
return {
|
|
114
115
|
provider: "gemini",
|
|
115
|
-
apiKey:
|
|
116
|
+
apiKey: geminiKey,
|
|
116
117
|
baseURL: GEMINI_BASE,
|
|
117
118
|
model: config.model ?? process.env.GEMINI_MODEL ?? GEMINI_DEFAULT_MODEL,
|
|
118
119
|
};
|
|
@@ -1,141 +1,245 @@
|
|
|
1
|
-
/* Technical Debt Report -
|
|
1
|
+
/* Technical Debt Report — Grafana-style dashboard */
|
|
2
2
|
:root {
|
|
3
|
-
--bg: #
|
|
4
|
-
--
|
|
5
|
-
--
|
|
6
|
-
--
|
|
7
|
-
--
|
|
8
|
-
--
|
|
3
|
+
--bg: #0b0c0e;
|
|
4
|
+
--bg-elevated: #111214;
|
|
5
|
+
--surface: #181b1f;
|
|
6
|
+
--surface-hover: #1e2127;
|
|
7
|
+
--border: #2c3239;
|
|
8
|
+
--border-subtle: #252a30;
|
|
9
|
+
--text: #e8e9ea;
|
|
10
|
+
--text-muted: #8b9199;
|
|
11
|
+
--link: #5794f2;
|
|
12
|
+
--radius: 4px;
|
|
13
|
+
--shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
|
9
14
|
}
|
|
10
15
|
|
|
11
|
-
[data-theme="
|
|
12
|
-
--bg: #
|
|
13
|
-
--
|
|
14
|
-
--
|
|
15
|
-
--
|
|
16
|
-
--
|
|
17
|
-
--
|
|
16
|
+
[data-theme="light"] {
|
|
17
|
+
--bg: #f4f5f5;
|
|
18
|
+
--bg-elevated: #fff;
|
|
19
|
+
--surface: #fff;
|
|
20
|
+
--surface-hover: #f9fafb;
|
|
21
|
+
--border: #d8d9da;
|
|
22
|
+
--border-subtle: #e8e9ea;
|
|
23
|
+
--text: #1e2022;
|
|
24
|
+
--text-muted: #6b7077;
|
|
25
|
+
--link: #3274d9;
|
|
26
|
+
--shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
|
|
18
27
|
}
|
|
19
28
|
|
|
20
29
|
* { box-sizing: border-box; }
|
|
21
30
|
|
|
22
31
|
body {
|
|
23
32
|
margin: 0;
|
|
24
|
-
font-family: system
|
|
33
|
+
font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
25
34
|
background: var(--bg);
|
|
26
35
|
color: var(--text);
|
|
27
36
|
min-height: 100vh;
|
|
28
|
-
font-size:
|
|
37
|
+
font-size: 13px;
|
|
29
38
|
line-height: 1.5;
|
|
39
|
+
-webkit-font-smoothing: antialiased;
|
|
30
40
|
}
|
|
31
41
|
|
|
32
|
-
.
|
|
33
|
-
|
|
34
|
-
margin: 0 auto;
|
|
35
|
-
padding: 1.5rem 1.25rem;
|
|
42
|
+
body.dashboard-page {
|
|
43
|
+
padding-bottom: 2rem;
|
|
36
44
|
}
|
|
37
45
|
|
|
38
|
-
/*
|
|
39
|
-
.
|
|
40
|
-
|
|
41
|
-
|
|
46
|
+
/* ——— Dashboard header (Grafana-style top bar) ——— */
|
|
47
|
+
.dashboard-header {
|
|
48
|
+
display: flex;
|
|
49
|
+
align-items: flex-start;
|
|
50
|
+
justify-content: space-between;
|
|
51
|
+
flex-wrap: wrap;
|
|
52
|
+
gap: 1rem;
|
|
53
|
+
padding: 1rem 1.5rem;
|
|
54
|
+
background: var(--bg-elevated);
|
|
42
55
|
border-bottom: 1px solid var(--border);
|
|
43
|
-
|
|
44
|
-
|
|
56
|
+
position: sticky;
|
|
57
|
+
top: 0;
|
|
58
|
+
z-index: 10;
|
|
45
59
|
}
|
|
46
60
|
|
|
47
|
-
.
|
|
61
|
+
.dashboard-header-left {
|
|
62
|
+
display: flex;
|
|
63
|
+
align-items: flex-start;
|
|
64
|
+
gap: 1rem;
|
|
65
|
+
min-width: 0;
|
|
66
|
+
flex: 1;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.dashboard-score-badge {
|
|
70
|
+
flex-shrink: 0;
|
|
71
|
+
line-height: 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.dashboard-score-badge .score-badge-svg {
|
|
75
|
+
width: 56px;
|
|
76
|
+
height: auto;
|
|
77
|
+
display: block;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.dashboard-score-badge .score-badge-num { font-size: 22px; }
|
|
81
|
+
.dashboard-score-badge .score-badge-of { font-size: 9px; }
|
|
82
|
+
|
|
83
|
+
.dashboard-hero {
|
|
84
|
+
min-width: 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.dashboard-title {
|
|
88
|
+
margin: 0 0 0.25rem;
|
|
89
|
+
font-size: 1.125rem;
|
|
90
|
+
font-weight: 600;
|
|
91
|
+
letter-spacing: -0.01em;
|
|
92
|
+
color: var(--text);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.dashboard-blurb {
|
|
96
|
+
margin: 0 0 0.35rem;
|
|
48
97
|
font-size: 14px;
|
|
49
|
-
|
|
98
|
+
line-height: 1.45;
|
|
50
99
|
color: var(--text);
|
|
51
|
-
|
|
100
|
+
max-width: 42em;
|
|
52
101
|
}
|
|
53
102
|
|
|
54
|
-
|
|
55
|
-
.hero-caption {
|
|
103
|
+
.dashboard-meta {
|
|
56
104
|
font-size: 12px;
|
|
57
105
|
color: var(--text-muted);
|
|
58
|
-
margin: 0 0 0.5rem;
|
|
59
|
-
text-align: center;
|
|
60
106
|
}
|
|
61
107
|
|
|
62
|
-
.
|
|
63
|
-
|
|
64
|
-
|
|
108
|
+
.dashboard-header-right {
|
|
109
|
+
display: flex;
|
|
110
|
+
flex-direction: column;
|
|
111
|
+
align-items: flex-end;
|
|
112
|
+
gap: 0.25rem;
|
|
113
|
+
flex-shrink: 0;
|
|
65
114
|
}
|
|
66
115
|
|
|
67
|
-
.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
116
|
+
.dashboard-stats {
|
|
117
|
+
font-size: 12px;
|
|
118
|
+
color: var(--text-muted);
|
|
119
|
+
white-space: nowrap;
|
|
71
120
|
}
|
|
72
121
|
|
|
73
|
-
.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
max-width: min(160px, 40vw);
|
|
122
|
+
.dashboard-date {
|
|
123
|
+
font-size: 11px;
|
|
124
|
+
color: var(--text-muted);
|
|
125
|
+
opacity: 0.9;
|
|
78
126
|
}
|
|
79
127
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
128
|
+
/* ——— Main content & grid ——— */
|
|
129
|
+
.dashboard-main {
|
|
130
|
+
max-width: 1200px;
|
|
131
|
+
margin: 0 auto;
|
|
132
|
+
padding: 1.25rem 1.5rem;
|
|
85
133
|
}
|
|
86
134
|
|
|
87
|
-
.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
letter-spacing: 0.08em;
|
|
92
|
-
text-transform: uppercase;
|
|
93
|
-
opacity: 0.95;
|
|
135
|
+
.dashboard-grid {
|
|
136
|
+
display: grid;
|
|
137
|
+
gap: 1rem;
|
|
138
|
+
margin-bottom: 1rem;
|
|
94
139
|
}
|
|
95
140
|
|
|
96
|
-
.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
.hero.tier-1 { --tier-bg: #c00; --tier-num: #fff; }
|
|
101
|
-
.hero.tier-2 { --tier-bg: #e85d00; --tier-num: #fff; }
|
|
102
|
-
.hero.tier-3 { --tier-bg: #b8860b; --tier-num: #fff; }
|
|
103
|
-
.hero.tier-4 { --tier-bg: #069; --tier-num: #fff; }
|
|
104
|
-
.hero.tier-5 { --tier-bg: #0a6b0a; --tier-num: #fff; }
|
|
141
|
+
.dashboard-grid-half {
|
|
142
|
+
grid-template-columns: repeat(2, 1fr);
|
|
143
|
+
}
|
|
105
144
|
|
|
106
|
-
|
|
107
|
-
.
|
|
108
|
-
display: grid;
|
|
109
|
-
grid-template-columns: repeat(4, 1fr);
|
|
110
|
-
gap: 0.75rem;
|
|
111
|
-
margin-bottom: 1.5rem;
|
|
145
|
+
@media (max-width: 900px) {
|
|
146
|
+
.dashboard-grid-half { grid-template-columns: 1fr; }
|
|
112
147
|
}
|
|
113
148
|
|
|
114
|
-
@media (max-width:
|
|
115
|
-
.
|
|
149
|
+
@media (max-width: 560px) {
|
|
150
|
+
.dashboard-header { padding: 0.75rem 1rem; }
|
|
151
|
+
.dashboard-main { padding: 1rem; }
|
|
116
152
|
}
|
|
117
153
|
|
|
118
|
-
|
|
154
|
+
/* ——— Panels (Grafana-style) ——— */
|
|
155
|
+
.panel {
|
|
119
156
|
background: var(--surface);
|
|
120
157
|
border: 1px solid var(--border);
|
|
158
|
+
border-radius: var(--radius);
|
|
159
|
+
box-shadow: var(--shadow);
|
|
160
|
+
overflow: hidden;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.panel-header {
|
|
121
164
|
padding: 0.75rem 1rem;
|
|
165
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
166
|
+
background: rgba(0, 0, 0, 0.03);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
[data-theme="light"] .panel-header {
|
|
170
|
+
background: rgba(0, 0, 0, 0.02);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.panel-title {
|
|
174
|
+
margin: 0;
|
|
175
|
+
font-size: 13px;
|
|
176
|
+
font-weight: 600;
|
|
177
|
+
color: var(--text);
|
|
178
|
+
letter-spacing: -0.01em;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.panel-desc {
|
|
182
|
+
margin: 0.25rem 0 0;
|
|
183
|
+
font-size: 12px;
|
|
184
|
+
color: var(--text-muted);
|
|
185
|
+
line-height: 1.4;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.panel-body {
|
|
189
|
+
padding: 1rem;
|
|
122
190
|
}
|
|
123
191
|
|
|
124
|
-
.
|
|
125
|
-
|
|
192
|
+
.panel-body-center {
|
|
193
|
+
text-align: center;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* ——— Heatmap panel ——— */
|
|
197
|
+
.panel-heatmap .panel-header-heatmap {
|
|
198
|
+
display: flex;
|
|
199
|
+
flex-wrap: wrap;
|
|
200
|
+
align-items: baseline;
|
|
201
|
+
gap: 0.75rem 1.25rem;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.panel-heatmap .panel-title { margin-right: 0.5rem; }
|
|
205
|
+
.panel-heatmap .panel-desc { margin: 0.15rem 0 0; flex-basis: 100%; }
|
|
206
|
+
|
|
207
|
+
.legend-inline {
|
|
208
|
+
margin: 0;
|
|
209
|
+
margin-left: auto;
|
|
210
|
+
}
|
|
126
211
|
|
|
127
|
-
|
|
128
|
-
|
|
212
|
+
.panel-body-heatmap {
|
|
213
|
+
padding: 1rem;
|
|
214
|
+
padding-top: 0.5rem;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.panel-body-heatmap #treemap {
|
|
218
|
+
border-radius: var(--radius);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* LLM panel accent */
|
|
222
|
+
.panel-llm {
|
|
223
|
+
border-left: 3px solid var(--link);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/* ——— Banner ——— */
|
|
227
|
+
.no-llm-banner {
|
|
228
|
+
width: 100%;
|
|
129
229
|
background: var(--surface);
|
|
130
|
-
border: 1px solid var(--border);
|
|
131
|
-
padding: 1rem
|
|
132
|
-
|
|
230
|
+
border-bottom: 1px solid var(--border);
|
|
231
|
+
padding: 0.5rem 1rem;
|
|
232
|
+
text-align: center;
|
|
133
233
|
}
|
|
134
234
|
|
|
135
|
-
.
|
|
136
|
-
|
|
235
|
+
.no-llm-banner .no-llm-cta {
|
|
236
|
+
font-size: 13px;
|
|
237
|
+
font-weight: normal;
|
|
238
|
+
color: var(--text-muted);
|
|
239
|
+
margin: 0;
|
|
240
|
+
}
|
|
137
241
|
|
|
138
|
-
/* Treemap */
|
|
242
|
+
/* ——— Treemap ——— */
|
|
139
243
|
#treemap {
|
|
140
244
|
display: flex;
|
|
141
245
|
flex-wrap: wrap;
|
|
@@ -154,10 +258,14 @@ body {
|
|
|
154
258
|
overflow: hidden;
|
|
155
259
|
text-overflow: ellipsis;
|
|
156
260
|
white-space: nowrap;
|
|
157
|
-
border: 1px solid rgba(0,0,0,0.
|
|
261
|
+
border: 1px solid rgba(0, 0, 0, 0.15);
|
|
262
|
+
border-radius: 2px;
|
|
158
263
|
}
|
|
159
264
|
|
|
160
|
-
.treemap-cell:hover {
|
|
265
|
+
.treemap-cell:hover {
|
|
266
|
+
outline: 2px solid var(--link);
|
|
267
|
+
outline-offset: 1px;
|
|
268
|
+
}
|
|
161
269
|
|
|
162
270
|
.treemap-cell[data-severity="critical"] { background: #c00; color: #fff; border-color: #900; }
|
|
163
271
|
.treemap-cell[data-severity="high"] { background: #e85d00; color: #fff; border-color: #b84a00; }
|
|
@@ -165,37 +273,87 @@ body {
|
|
|
165
273
|
.treemap-cell[data-severity="low"] { background: #0a6b0a; color: #fff; border-color: #064906; }
|
|
166
274
|
.treemap-cell[data-severity="none"] { background: var(--border); color: var(--text-muted); border-color: var(--border); }
|
|
167
275
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
276
|
+
.legend {
|
|
277
|
+
display: flex;
|
|
278
|
+
flex-wrap: wrap;
|
|
279
|
+
gap: 1rem;
|
|
280
|
+
align-items: center;
|
|
281
|
+
margin-bottom: 0.75rem;
|
|
282
|
+
font-size: 12px;
|
|
283
|
+
color: var(--text-muted);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.legend span { display: inline-flex; align-items: center; gap: 0.35rem; }
|
|
287
|
+
.legend .swatch { width: 10px; height: 10px; border-radius: 2px; }
|
|
172
288
|
.legend .swatch-crit { background: #c00; }
|
|
173
289
|
.legend .swatch-high { background: #e85d00; }
|
|
174
290
|
.legend .swatch-med { background: #b8860b; }
|
|
175
291
|
.legend .swatch-low { background: #0a6b0a; }
|
|
176
292
|
.legend .swatch-none { background: var(--border); }
|
|
177
293
|
|
|
178
|
-
/*
|
|
179
|
-
.
|
|
294
|
+
/* ——— Priority lists ——— */
|
|
295
|
+
.priority-list {
|
|
296
|
+
list-style: none;
|
|
297
|
+
padding: 0;
|
|
298
|
+
margin: 0;
|
|
299
|
+
font-size: 12px;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.priority-list li {
|
|
303
|
+
padding: 0.4rem 0;
|
|
304
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
305
|
+
color: var(--text);
|
|
306
|
+
font-family: ui-monospace, monospace;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.priority-list li:last-child { border-bottom: none; }
|
|
310
|
+
|
|
311
|
+
/* ——— Debt list ——— */
|
|
312
|
+
.debt-list {
|
|
313
|
+
list-style: none;
|
|
314
|
+
padding: 0;
|
|
315
|
+
margin: 0;
|
|
316
|
+
}
|
|
180
317
|
|
|
181
318
|
.debt-list li {
|
|
182
|
-
border-bottom: 1px solid var(--border);
|
|
183
|
-
padding: 0.
|
|
319
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
320
|
+
padding: 0.65rem 0;
|
|
184
321
|
cursor: pointer;
|
|
322
|
+
transition: background 0.12s ease;
|
|
185
323
|
}
|
|
186
324
|
|
|
187
325
|
.debt-list li:last-child { border-bottom: none; }
|
|
188
|
-
.debt-list li:hover { background: var(--
|
|
189
|
-
|
|
190
|
-
.debt-list .
|
|
326
|
+
.debt-list li:hover { background: var(--surface-hover); }
|
|
327
|
+
|
|
328
|
+
.debt-list .title { font-weight: 600; margin-bottom: 0.2rem; }
|
|
329
|
+
.debt-list .meta { font-size: 12px; color: var(--text-muted); display: block; margin-top: 0.25rem; font-family: ui-monospace, monospace; }
|
|
330
|
+
.debt-list-explanation {
|
|
331
|
+
font-size: 12px;
|
|
332
|
+
color: var(--text-muted);
|
|
333
|
+
margin: 0.35rem 0 0;
|
|
334
|
+
line-height: 1.4;
|
|
335
|
+
display: -webkit-box;
|
|
336
|
+
-webkit-line-clamp: 2;
|
|
337
|
+
-webkit-box-orient: vertical;
|
|
338
|
+
overflow: hidden;
|
|
339
|
+
}
|
|
191
340
|
.debt-list .insight { font-size: 13px; color: var(--text-muted); margin-top: 0.25rem; line-height: 1.4; white-space: pre-wrap; word-break: break-word; }
|
|
192
|
-
.debt-list-ratings { display: flex; align-items: center; gap: 1rem; margin: 0.25rem 0; flex-wrap: wrap; }
|
|
341
|
+
.debt-list-ratings { display: flex; align-items: center; gap: 1rem; margin: 0.25rem 0 0; flex-wrap: wrap; }
|
|
193
342
|
.debt-list-rating { display: inline-flex; align-items: center; gap: 0.35rem; }
|
|
194
343
|
.debt-list-rating-label { font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-muted); }
|
|
195
|
-
.debt-list-llm-none { color: var(--text-muted); font-size:
|
|
344
|
+
.debt-list-llm-none { color: var(--text-muted); font-size: 12px; }
|
|
196
345
|
|
|
197
346
|
/* Badges */
|
|
198
|
-
.badge {
|
|
347
|
+
.badge {
|
|
348
|
+
display: inline-block;
|
|
349
|
+
padding: 0.15em 0.45em;
|
|
350
|
+
font-size: 10px;
|
|
351
|
+
font-weight: 600;
|
|
352
|
+
text-transform: uppercase;
|
|
353
|
+
letter-spacing: 0.03em;
|
|
354
|
+
border-radius: 3px;
|
|
355
|
+
}
|
|
356
|
+
|
|
199
357
|
.badge-critical { background: #c00; color: #fff; }
|
|
200
358
|
.badge-high { background: #e85d00; color: #fff; }
|
|
201
359
|
.badge-medium { background: #b8860b; color: #fff; }
|
|
@@ -203,11 +361,11 @@ body {
|
|
|
203
361
|
.badge-none { background: var(--border); color: var(--text-muted); }
|
|
204
362
|
.badge-llm { background: var(--link); color: #fff; }
|
|
205
363
|
|
|
206
|
-
/* Detail modal */
|
|
364
|
+
/* ——— Detail modal ——— */
|
|
207
365
|
#detail {
|
|
208
366
|
position: fixed;
|
|
209
367
|
inset: 0;
|
|
210
|
-
background: rgba(0,0,0,0.
|
|
368
|
+
background: rgba(0, 0, 0, 0.55);
|
|
211
369
|
display: none;
|
|
212
370
|
align-items: center;
|
|
213
371
|
justify-content: center;
|
|
@@ -220,7 +378,9 @@ body {
|
|
|
220
378
|
#detail .panel {
|
|
221
379
|
background: var(--surface);
|
|
222
380
|
border: 1px solid var(--border);
|
|
223
|
-
|
|
381
|
+
border-radius: var(--radius);
|
|
382
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.35);
|
|
383
|
+
max-width: 540px;
|
|
224
384
|
width: 100%;
|
|
225
385
|
max-height: 90vh;
|
|
226
386
|
overflow-y: auto;
|
|
@@ -229,7 +389,7 @@ body {
|
|
|
229
389
|
-webkit-overflow-scrolling: touch;
|
|
230
390
|
}
|
|
231
391
|
|
|
232
|
-
#detail .panel h3 { margin: 0 0 0.35rem; font-size:
|
|
392
|
+
#detail .panel h3 { margin: 0 0 0.35rem; font-size: 14px; font-weight: 600; }
|
|
233
393
|
#detail .panel .file { font-family: ui-monospace, monospace; font-size: 13px; color: var(--link); }
|
|
234
394
|
#detail .panel .close-hint { margin-top: 0.75rem; font-size: 12px; color: var(--text-muted); }
|
|
235
395
|
|
|
@@ -250,6 +410,10 @@ body {
|
|
|
250
410
|
#detail .panel .detail-explanation .detail-llm-label { margin-top: 0.5rem; margin-bottom: 0.25rem; }
|
|
251
411
|
#detail .panel .detail-explanation .detail-no-llm { font-style: italic; color: var(--text-muted); }
|
|
252
412
|
#detail .panel .detail-explanation .detail-static-desc { margin-top: 0.5rem; font-size: 12px; color: var(--text-muted); }
|
|
413
|
+
#detail .panel .detail-explanation .detail-issues-list { margin: 0.25rem 0 0 0; padding-left: 1.25rem; list-style: none; }
|
|
414
|
+
#detail .panel .detail-explanation .detail-issues-list > li { margin-bottom: 0.35rem; }
|
|
415
|
+
#detail .panel .detail-explanation .detail-issues-list .detail-issue-desc { margin: 0.15rem 0 0 1rem; font-size: 11px; opacity: 0.9; }
|
|
416
|
+
#detail .panel .detail-explanation .detail-issues-list .detail-line { color: var(--text-muted); font-size: 11px; }
|
|
253
417
|
|
|
254
418
|
#detail .panel .file-assessment {
|
|
255
419
|
margin-top: 0.75rem;
|
|
@@ -265,14 +429,14 @@ body {
|
|
|
265
429
|
#detail .panel .file-assessment strong { color: var(--text); font-size: 12px; }
|
|
266
430
|
#detail .panel .suggested-code { margin-top: 0.75rem; }
|
|
267
431
|
|
|
268
|
-
/* Code blocks
|
|
432
|
+
/* ——— Code blocks ——— */
|
|
269
433
|
.code-block {
|
|
270
434
|
margin: 0.5rem 0;
|
|
271
435
|
padding: 0.75rem 1rem;
|
|
272
436
|
background: var(--bg);
|
|
273
437
|
border: 1px solid var(--border);
|
|
274
|
-
border-radius:
|
|
275
|
-
font-size:
|
|
438
|
+
border-radius: var(--radius);
|
|
439
|
+
font-size: 12px;
|
|
276
440
|
overflow-x: auto;
|
|
277
441
|
overflow-y: auto;
|
|
278
442
|
max-height: 20em;
|
|
@@ -280,43 +444,36 @@ body {
|
|
|
280
444
|
|
|
281
445
|
.code-block .lang-label {
|
|
282
446
|
display: block;
|
|
283
|
-
font-size:
|
|
447
|
+
font-size: 10px;
|
|
284
448
|
font-family: system-ui, sans-serif;
|
|
285
449
|
color: var(--text-muted);
|
|
286
450
|
margin-bottom: 0.5rem;
|
|
287
451
|
text-transform: uppercase;
|
|
288
|
-
letter-spacing: 0.
|
|
452
|
+
letter-spacing: 0.04em;
|
|
289
453
|
}
|
|
290
454
|
|
|
291
455
|
.code-block pre { margin: 0; white-space: pre; overflow-x: auto; min-width: min-content; }
|
|
292
456
|
.code-block code {
|
|
293
|
-
font-family: ui-monospace,
|
|
457
|
+
font-family: ui-monospace, "SF Mono", "Cascadia Code", Consolas, monospace;
|
|
294
458
|
font-size: 12px;
|
|
295
459
|
line-height: 1.5;
|
|
296
460
|
color: var(--text);
|
|
297
461
|
display: block;
|
|
298
462
|
}
|
|
299
463
|
|
|
300
|
-
#detail .panel .code-block {
|
|
301
|
-
max-height: 16em;
|
|
302
|
-
}
|
|
303
|
-
|
|
464
|
+
#detail .panel .code-block { max-height: 16em; }
|
|
304
465
|
#detail .panel .suggested-code .code-block { margin: 0.35rem 0 0; background: var(--surface); }
|
|
305
466
|
#detail .panel .file-assessment .code-block { margin: 0.5rem 0 0; background: var(--surface); }
|
|
306
467
|
|
|
307
|
-
/* LLM
|
|
308
|
-
.llm-
|
|
309
|
-
|
|
310
|
-
|
|
468
|
+
/* ——— LLM output ——— */
|
|
469
|
+
.llm-output .llm-prose {
|
|
470
|
+
margin: 0 0 0.75rem;
|
|
471
|
+
line-height: 1.55;
|
|
472
|
+
white-space: pre-wrap;
|
|
473
|
+
word-break: break-word;
|
|
474
|
+
color: var(--text);
|
|
475
|
+
}
|
|
311
476
|
|
|
312
|
-
.llm-output .llm-prose { margin: 0 0 0.75rem; line-height: 1.5; white-space: pre-wrap; word-break: break-word; }
|
|
313
477
|
.llm-output .llm-prose:last-child { margin-bottom: 0; }
|
|
314
478
|
|
|
315
|
-
.llm-
|
|
316
|
-
.llm-next-steps ul { margin: 0; padding-left: 1.25rem; color: var(--text); line-height: 1.5; white-space: pre-wrap; word-break: break-word; }
|
|
317
|
-
|
|
318
|
-
/* Priority matrix */
|
|
319
|
-
.priority-matrix { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; margin-top: 0.75rem; }
|
|
320
|
-
.priority-matrix .quadrant { padding: 0.75rem; border: 1px solid var(--border); }
|
|
321
|
-
.priority-matrix .quadrant h4 { margin: 0 0 0.35rem; font-size: 0.9rem; font-weight: bold; }
|
|
322
|
-
.priority-matrix .quadrant p { margin: 0; font-size: 13px; color: var(--text-muted); }
|
|
479
|
+
.panel-llm .panel-body .llm-output { margin: 0; }
|
|
@@ -166,58 +166,128 @@ document.getElementById("q2").innerHTML = highImpact
|
|
|
166
166
|
.map(function (d) { return '<li style="font-size:0.8rem">' + escapeHtml(d.file) + "</li>"; })
|
|
167
167
|
.join("");
|
|
168
168
|
|
|
169
|
-
// Debt list:
|
|
170
|
-
var sev = { critical: 4, high: 3, medium: 2, low: 1 };
|
|
169
|
+
// Debt list: every file rated above none by static OR LLM; show both ratings and short explanation
|
|
170
|
+
var sev = { critical: 4, high: 3, medium: 2, low: 1, none: 0 };
|
|
171
171
|
var list = document.getElementById("debtList");
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
172
|
+
|
|
173
|
+
function severityNum(s) { return sev[s] || 0; }
|
|
174
|
+
function fileWorstStatic(items) {
|
|
175
|
+
if (!items || !items.length) return 0;
|
|
176
|
+
return Math.max.apply(null, items.map(function (d) { return severityNum(d.severity); }));
|
|
177
|
+
}
|
|
178
|
+
function fileWorstLlm(metric) {
|
|
179
|
+
if (!metric || !metric.llmSeverity || metric.llmSeverity === "none") return 0;
|
|
180
|
+
return severityNum(metric.llmSeverity);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
var filesFromStatic = new Set(debtByFile.keys());
|
|
184
|
+
var filesFromLlm = new Set();
|
|
185
|
+
DATA.fileMetrics.forEach(function (m) {
|
|
186
|
+
if (m.llmSeverity && m.llmSeverity !== "none") filesFromLlm.add(m.file);
|
|
187
|
+
});
|
|
188
|
+
var fileSet = new Set([].concat(Array.from(filesFromStatic), Array.from(filesFromLlm)));
|
|
189
|
+
|
|
190
|
+
var filesWithDebt = Array.from(fileSet).sort(function (fa, fb) {
|
|
191
|
+
var itemsA = debtByFile.get(fa) || [];
|
|
192
|
+
var itemsB = debtByFile.get(fb) || [];
|
|
193
|
+
var metricA = DATA.fileMetrics.find(function (m) { return m.file === fa; });
|
|
194
|
+
var metricB = DATA.fileMetrics.find(function (m) { return m.file === fb; });
|
|
195
|
+
var worstA = Math.max(fileWorstStatic(itemsA), fileWorstLlm(metricA));
|
|
196
|
+
var worstB = Math.max(fileWorstStatic(itemsB), fileWorstLlm(metricB));
|
|
197
|
+
if (worstB !== worstA) return worstB - worstA;
|
|
198
|
+
return fa.localeCompare(fb);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
function firstLine(text) {
|
|
202
|
+
if (!text || !String(text).trim()) return "";
|
|
203
|
+
return String(text).trim().split(/\n/)[0].trim().slice(0, 120);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
filesWithDebt.forEach(function (file) {
|
|
207
|
+
var items = debtByFile.get(file) || [];
|
|
208
|
+
var fileM = DATA.fileMetrics.find(function (m) { return m.file === file; });
|
|
209
|
+
var worstSeverityVal = items.length ? items.reduce(function (best, d) {
|
|
210
|
+
return severityNum(d.severity) > severityNum(best.severity) ? d : best;
|
|
211
|
+
}, items[0]) : null;
|
|
212
|
+
var staticSeverity = worstSeverityVal ? worstSeverityVal.severity : null;
|
|
213
|
+
var fileLlmSeverity = fileM && fileM.llmSeverity && fileM.llmSeverity !== "none" ? fileM.llmSeverity : null;
|
|
214
|
+
|
|
215
|
+
var staticBadge = staticSeverity
|
|
216
|
+
? '<span class="badge badge-' + staticSeverity + '" title="Static (worst of ' + items.length + ')">' + staticSeverity + "</span>"
|
|
181
217
|
: '<span class="debt-list-llm-none">—</span>';
|
|
218
|
+
var llmBadge = fileLlmSeverity
|
|
219
|
+
? '<span class="badge badge-' + fileLlmSeverity + '" title="LLM">' + fileLlmSeverity + "</span>"
|
|
220
|
+
: '<span class="debt-list-llm-none">—</span>';
|
|
221
|
+
|
|
222
|
+
var explanation = "";
|
|
223
|
+
if (items.length && worstSeverityVal) {
|
|
224
|
+
explanation = escapeHtml(firstLine(worstSeverityVal.title || worstSeverityVal.description || ""));
|
|
225
|
+
if (items.length > 1) explanation += " (+" + (items.length - 1) + " more)";
|
|
226
|
+
}
|
|
227
|
+
if (fileM && (fileM.llmAssessment || fileM.llmRawAssessment)) {
|
|
228
|
+
var llmBlurb = firstLine(stripTrailingSeverityAndScore(fileM.llmRawAssessment || fileM.llmAssessment || ""));
|
|
229
|
+
if (llmBlurb) explanation = explanation ? explanation + " · " + escapeHtml(llmBlurb) : escapeHtml(llmBlurb);
|
|
230
|
+
}
|
|
231
|
+
if (!explanation) explanation = "Rated by static or LLM.";
|
|
232
|
+
|
|
182
233
|
var ratingsRow =
|
|
183
234
|
'<div class="debt-list-ratings">' +
|
|
184
235
|
'<span class="debt-list-rating"><span class="debt-list-rating-label">Static</span> ' + staticBadge + "</span>" +
|
|
185
|
-
'<span class="debt-list-rating"><span class="debt-list-rating-label">LLM</span> ' +
|
|
236
|
+
'<span class="debt-list-rating"><span class="debt-list-rating-label">LLM</span> ' + llmBadge + "</span>" +
|
|
186
237
|
"</div>";
|
|
238
|
+
var li = document.createElement("li");
|
|
187
239
|
li.innerHTML =
|
|
188
|
-
'<span class="title">' +
|
|
189
|
-
escapeHtml(d.title) +
|
|
190
|
-
"</span> " +
|
|
240
|
+
'<span class="title">' + escapeHtml(file.split("/").pop() || file) + "</span> " +
|
|
191
241
|
ratingsRow +
|
|
192
|
-
'<span class="meta">' +
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
"</span>";
|
|
196
|
-
li.addEventListener("click", function () { showDetail(d.file, [d]); });
|
|
242
|
+
'<span class="meta">' + escapeHtml(file) + "</span>" +
|
|
243
|
+
'<p class="debt-list-explanation">' + explanation + "</p>";
|
|
244
|
+
li.addEventListener("click", function () { showDetail(file, items); });
|
|
197
245
|
list.appendChild(li);
|
|
198
246
|
});
|
|
199
247
|
|
|
200
248
|
function showDetail(file, items) {
|
|
201
249
|
var panel = document.getElementById("detail");
|
|
202
|
-
var item = items.length ? items[0] : null;
|
|
203
250
|
var fileMetric = DATA.fileMetrics.find(function (m) { return m.file === file; });
|
|
251
|
+
var worstItem = items.length ? items.reduce(function (best, d) {
|
|
252
|
+
return severityNum(d.severity) > severityNum(best.severity) ? d : best;
|
|
253
|
+
}, items[0]) : null;
|
|
254
|
+
var staticSeverity = worstItem ? worstItem.severity : null;
|
|
204
255
|
|
|
205
|
-
|
|
256
|
+
var titleText = items.length === 1
|
|
257
|
+
? (items[0].title || "Debt item")
|
|
258
|
+
: items.length > 1
|
|
259
|
+
? items.length + " static issues"
|
|
260
|
+
: (fileMetric && fileMetric.llmSeverity ? "LLM assessment" : "File details");
|
|
261
|
+
document.getElementById("detailTitle").textContent = titleText;
|
|
206
262
|
document.getElementById("detailFile").textContent = file;
|
|
207
263
|
|
|
208
264
|
var explanationEl = document.getElementById("detailExplanation");
|
|
209
265
|
var parts = [];
|
|
210
|
-
var staticSev = item ? item.severity : "—";
|
|
211
|
-
var llmSev = fileMetric && fileMetric.llmSeverity ? fileMetric.llmSeverity : "—";
|
|
212
266
|
parts.push(
|
|
213
267
|
'<div class="detail-severities">' +
|
|
214
|
-
'<span class="detail-sev"><strong>Static</strong>
|
|
268
|
+
'<span class="detail-sev"><strong>Static</strong> ' +
|
|
269
|
+
(staticSeverity ? '<span class="badge badge-' + staticSeverity + '">' + staticSeverity + "</span> (worst of " + items.length + ")" : "<span class=\"debt-list-llm-none\">\u2014</span>") +
|
|
270
|
+
"</span> " +
|
|
215
271
|
'<span class="detail-sev"><strong>LLM</strong> ' +
|
|
216
|
-
(fileMetric && fileMetric.llmSeverity ? '<span class="badge badge-' + fileMetric.llmSeverity + '">' + fileMetric.llmSeverity + "</span>" : "<span class=\"debt-list-llm-none\"
|
|
272
|
+
(fileMetric && fileMetric.llmSeverity && fileMetric.llmSeverity !== "none" ? '<span class="badge badge-' + fileMetric.llmSeverity + '">' + fileMetric.llmSeverity + "</span>" : "<span class=\"debt-list-llm-none\">\u2014</span>") +
|
|
217
273
|
"</span></div>"
|
|
218
274
|
);
|
|
219
|
-
|
|
220
|
-
|
|
275
|
+
parts.push('<div class="detail-static-desc"><strong>Static issues</strong><ul class="detail-issues-list">');
|
|
276
|
+
if (items.length) {
|
|
277
|
+
items.forEach(function (item) {
|
|
278
|
+
parts.push(
|
|
279
|
+
'<li><span class="badge badge-' + item.severity + '">' + item.severity + "</span> " +
|
|
280
|
+
escapeHtml(item.title || "Issue") +
|
|
281
|
+
(item.line ? " <span class=\"detail-line\">line " + item.line + "</span>" : "")
|
|
282
|
+
);
|
|
283
|
+
if (item.description)
|
|
284
|
+
parts.push('<div class="detail-issue-desc">' + escapeHtml(item.description).replace(/\n/g, "<br>") + "</div>");
|
|
285
|
+
parts.push("</li>");
|
|
286
|
+
});
|
|
287
|
+
} else {
|
|
288
|
+
parts.push("<li class=\"detail-no-llm\">No static issues for this file.</li>");
|
|
289
|
+
}
|
|
290
|
+
parts.push("</ul></div>");
|
|
221
291
|
parts.push('<div class="detail-llm-label"><strong>LLM assessment</strong></div>');
|
|
222
292
|
if (fileMetric && (fileMetric.llmRawAssessment || fileMetric.llmAssessment)) {
|
|
223
293
|
if (fileMetric.llmRawAssessment)
|
package/dist/reports/html.js
CHANGED
|
@@ -78,18 +78,22 @@ function buildHtml(run, title, darkMode, css, script) {
|
|
|
78
78
|
};
|
|
79
79
|
const tierColor = tierColors[cleanliness.tier] ?? "#666";
|
|
80
80
|
const scoreBadgeSvg = buildScoreBadgeSvg(cleanliness.tier, tierColor);
|
|
81
|
-
const
|
|
81
|
+
const llmPanelHtml = run.llmOverallAssessment || run.llmOverallRaw
|
|
82
82
|
? `
|
|
83
|
-
<div class="
|
|
84
|
-
<
|
|
85
|
-
|
|
83
|
+
<div class="panel panel-llm">
|
|
84
|
+
<div class="panel-header">
|
|
85
|
+
<h2 class="panel-title">LLM overall assessment</h2>
|
|
86
|
+
</div>
|
|
87
|
+
<div class="panel-body">
|
|
88
|
+
<div class="llm-output">${run.llmOverallAssessment
|
|
86
89
|
? renderLlmOutputToHtml(run.llmOverallAssessment)
|
|
87
90
|
: '<div class="llm-prose">' +
|
|
88
91
|
escapeHtml(stripTrailingSeverityAndScore(run.llmOverallRaw ?? "")).replace(/\n/g, "<br>") +
|
|
89
92
|
"</div>"}</div>
|
|
90
|
-
|
|
91
|
-
|
|
93
|
+
</div>
|
|
94
|
+
</div>`
|
|
92
95
|
: "";
|
|
96
|
+
const statsLine = `${run.fileMetrics.length} files · ${run.debtItems.length} items · ${highCriticalCount} high/crit · ${hotspotCount} hotspots`;
|
|
93
97
|
return `<!DOCTYPE html>
|
|
94
98
|
<html lang="en" data-theme="${theme}">
|
|
95
99
|
<head>
|
|
@@ -104,54 +108,74 @@ function buildHtml(run, title, darkMode, css, script) {
|
|
|
104
108
|
<meta name="twitter:description" content="${escapeHtml(cleanliness.label)}: ${escapeHtml(cleanliness.description)}" />
|
|
105
109
|
<style>${css}</style>
|
|
106
110
|
</head>
|
|
107
|
-
<body>
|
|
111
|
+
<body class="dashboard-page">
|
|
108
112
|
${!hasLlm ? `<div class="no-llm-banner"><p class="no-llm-cta">Analysis run without LLM — for full results, run with LLM</p></div>` : ""}
|
|
109
|
-
<
|
|
110
|
-
<
|
|
111
|
-
|
|
112
|
-
<div class="
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
<header class="dashboard-header">
|
|
114
|
+
<div class="dashboard-header-left">
|
|
115
|
+
<div class="dashboard-score-badge tier-${cleanliness.tier}" aria-label="Score ${cleanliness.tier} of 5">${scoreBadgeSvg}</div>
|
|
116
|
+
<div class="dashboard-hero">
|
|
117
|
+
<h1 class="dashboard-title">${escapeHtml(title)}</h1>
|
|
118
|
+
<p class="dashboard-blurb">${escapeHtml(cleanliness.description)}</p>
|
|
119
|
+
<span class="dashboard-meta">${escapeHtml(run.repoPath)}</span>
|
|
120
|
+
</div>
|
|
116
121
|
</div>
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
<
|
|
120
|
-
<div class="card"><div class="value">${run.debtItems.length}</div><div class="label">Debt items</div></div>
|
|
121
|
-
<div class="card"><div class="value">${highCriticalCount}</div><div class="label">High / Critical</div></div>
|
|
122
|
-
<div class="card"><div class="value">${hotspotCount}</div><div class="label">Hotspots</div></div>
|
|
122
|
+
<div class="dashboard-header-right">
|
|
123
|
+
<span class="dashboard-stats">${statsLine}</span>
|
|
124
|
+
<span class="dashboard-date">${run.completedAt ?? run.startedAt}</span>
|
|
123
125
|
</div>
|
|
126
|
+
</header>
|
|
124
127
|
|
|
125
|
-
|
|
128
|
+
<main class="dashboard-main">
|
|
129
|
+
${llmPanelHtml}
|
|
126
130
|
|
|
127
|
-
<div class="
|
|
128
|
-
<
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
<
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
131
|
+
<div class="panel panel-heatmap">
|
|
132
|
+
<div class="panel-header panel-header-heatmap">
|
|
133
|
+
<h2 class="panel-title">Files by debt</h2>
|
|
134
|
+
<p class="panel-desc">Size = complexity + churn. Color = severity (static or LLM). Click for details.</p>
|
|
135
|
+
<div class="legend legend-inline">
|
|
136
|
+
<span><span class="swatch swatch-crit"></span> Critical</span>
|
|
137
|
+
<span><span class="swatch swatch-high"></span> High</span>
|
|
138
|
+
<span><span class="swatch swatch-med"></span> Medium</span>
|
|
139
|
+
<span><span class="swatch swatch-low"></span> Low</span>
|
|
140
|
+
<span><span class="swatch swatch-none"></span> None</span>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
<div class="panel-body panel-body-heatmap">
|
|
144
|
+
<div id="treemap"></div>
|
|
136
145
|
</div>
|
|
137
|
-
<div id="treemap"></div>
|
|
138
146
|
</div>
|
|
139
147
|
|
|
140
|
-
<div class="
|
|
141
|
-
<
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
148
|
+
<div class="dashboard-grid dashboard-grid-half">
|
|
149
|
+
<div class="panel">
|
|
150
|
+
<div class="panel-header">
|
|
151
|
+
<h2 class="panel-title">High impact, easier</h2>
|
|
152
|
+
<p class="panel-desc">High severity in smaller files.</p>
|
|
153
|
+
</div>
|
|
154
|
+
<div class="panel-body">
|
|
155
|
+
<ul id="q1" class="priority-list"></ul>
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
<div class="panel">
|
|
159
|
+
<div class="panel-header">
|
|
160
|
+
<h2 class="panel-title">High impact, harder</h2>
|
|
161
|
+
<p class="panel-desc">Critical or hotspot files.</p>
|
|
162
|
+
</div>
|
|
163
|
+
<div class="panel-body">
|
|
164
|
+
<ul id="q2" class="priority-list"></ul>
|
|
165
|
+
</div>
|
|
146
166
|
</div>
|
|
147
167
|
</div>
|
|
148
168
|
|
|
149
|
-
<div class="
|
|
150
|
-
<
|
|
151
|
-
|
|
152
|
-
|
|
169
|
+
<div class="panel">
|
|
170
|
+
<div class="panel-header">
|
|
171
|
+
<h2 class="panel-title">Files with debt (static or LLM)</h2>
|
|
172
|
+
<p class="panel-desc">Every file rated above none by static analysis or LLM. Click a row for full ratings and explanations.</p>
|
|
173
|
+
</div>
|
|
174
|
+
<div class="panel-body">
|
|
175
|
+
<ul class="debt-list" id="debtList"></ul>
|
|
176
|
+
</div>
|
|
153
177
|
</div>
|
|
154
|
-
</
|
|
178
|
+
</main>
|
|
155
179
|
|
|
156
180
|
<div id="detail">
|
|
157
181
|
<div class="panel">
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tech-debt-visualizer",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Language-agnostic CLI that analyzes repos and generates interactive technical debt visualizations with AI-powered insights",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|