tech-debt-visualizer 0.1.3 → 0.1.4
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/dist/reports/html.js +52 -103
- package/package.json +1 -1
package/dist/reports/html.js
CHANGED
|
@@ -17,10 +17,11 @@ function buildHtml(run, title, darkMode) {
|
|
|
17
17
|
const theme = darkMode ? "dark" : "light";
|
|
18
18
|
const debtScore = getDebtScore(run);
|
|
19
19
|
const cleanliness = getCleanlinessTier(debtScore);
|
|
20
|
+
const hasLlm = !!(run.llmOverallAssessment || (run.llmNextSteps && run.llmNextSteps.length));
|
|
20
21
|
const dataJson = JSON.stringify({
|
|
21
22
|
fileMetrics: run.fileMetrics,
|
|
22
23
|
debtItems: run.debtItems,
|
|
23
|
-
|
|
24
|
+
hasLlm,
|
|
24
25
|
llmOverallAssessment: run.llmOverallAssessment ?? null,
|
|
25
26
|
llmNextSteps: run.llmNextSteps ?? null,
|
|
26
27
|
summary: {
|
|
@@ -40,7 +41,6 @@ function buildHtml(run, title, darkMode) {
|
|
|
40
41
|
<meta charset="UTF-8" />
|
|
41
42
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
42
43
|
<title>${escapeHtml(title)}</title>
|
|
43
|
-
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
|
|
44
44
|
<style>
|
|
45
45
|
:root {
|
|
46
46
|
--bg: #0f0f12;
|
|
@@ -72,34 +72,34 @@ function buildHtml(run, title, darkMode) {
|
|
|
72
72
|
letter-spacing: -0.01em;
|
|
73
73
|
}
|
|
74
74
|
.container { max-width: 1100px; margin: 0 auto; padding: 2.5rem 2rem; }
|
|
75
|
-
.
|
|
76
|
-
|
|
77
|
-
display: flex;
|
|
78
|
-
align-items: stretch;
|
|
79
|
-
gap: 0;
|
|
80
|
-
margin-bottom: 2.5rem;
|
|
81
|
-
border-radius: 16px;
|
|
82
|
-
overflow: hidden;
|
|
75
|
+
.no-llm-banner {
|
|
76
|
+
width: 100%;
|
|
83
77
|
background: var(--surface);
|
|
84
|
-
border:
|
|
78
|
+
border-bottom: 2px solid var(--border);
|
|
79
|
+
padding: 0.75rem 1.5rem;
|
|
80
|
+
text-align: center;
|
|
85
81
|
}
|
|
86
|
-
.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
82
|
+
.no-llm-banner .no-llm-cta { font-size: 1rem; font-weight: 600; color: var(--text); margin: 0; }
|
|
83
|
+
.hero-caption { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-muted); margin: 0 0 1rem; font-weight: 600; text-align: center; }
|
|
84
|
+
.hero {
|
|
85
|
+
text-align: center;
|
|
86
|
+
margin-bottom: 2rem;
|
|
87
|
+
}
|
|
88
|
+
.score-badge {
|
|
89
|
+
display: inline-block;
|
|
90
|
+
width: 120px;
|
|
91
|
+
padding: 1rem 0.5rem 0.85rem;
|
|
94
92
|
background: var(--tier-bg);
|
|
95
|
-
|
|
93
|
+
color: var(--tier-fg);
|
|
94
|
+
border: 2px solid var(--tier-fg);
|
|
95
|
+
border-radius: 8px;
|
|
96
|
+
margin-bottom: 0.75rem;
|
|
96
97
|
}
|
|
97
|
-
.
|
|
98
|
-
.
|
|
99
|
-
.hero-
|
|
100
|
-
.hero
|
|
101
|
-
.hero
|
|
102
|
-
.hero-body .report-meta { font-size: 0.8rem; color: var(--text-muted); margin-top: 1.25rem; }
|
|
98
|
+
.score-badge .num { display: block; font-size: 3rem; font-weight: 700; line-height: 1; letter-spacing: -0.03em; text-align: center; }
|
|
99
|
+
.score-badge .of { display: block; font-size: 0.75rem; font-weight: 600; text-align: center; text-transform: uppercase; letter-spacing: 0.08em; margin-top: 0.25rem; opacity: 0.9; }
|
|
100
|
+
.hero .score-label { font-size: 1.25rem; font-weight: 700; color: var(--text); margin: 0 0 0.25rem; letter-spacing: -0.01em; }
|
|
101
|
+
.hero .score-desc { font-size: 0.9rem; color: var(--text-muted); line-height: 1.5; margin: 0 0 0.5rem; max-width: 380px; margin-left: auto; margin-right: auto; }
|
|
102
|
+
.hero .report-meta { font-size: 0.8rem; color: var(--text-muted); }
|
|
103
103
|
.hero.tier-1 { --tier-bg: #1c1917; --tier-fg: #f87171; }
|
|
104
104
|
.hero.tier-2 { --tier-bg: #1c1917; --tier-fg: #fb923c; }
|
|
105
105
|
.hero.tier-3 { --tier-bg: #1c1917; --tier-fg: #facc15; }
|
|
@@ -138,28 +138,29 @@ function buildHtml(run, title, darkMode) {
|
|
|
138
138
|
#treemap {
|
|
139
139
|
display: flex;
|
|
140
140
|
flex-wrap: wrap;
|
|
141
|
-
gap:
|
|
142
|
-
min-height:
|
|
141
|
+
gap: 8px;
|
|
142
|
+
min-height: 200px;
|
|
143
|
+
padding: 0.5rem 0;
|
|
143
144
|
}
|
|
144
145
|
.treemap-cell {
|
|
145
|
-
border-radius:
|
|
146
|
+
border-radius: 8px;
|
|
146
147
|
display: flex;
|
|
147
148
|
align-items: flex-end;
|
|
148
|
-
|
|
149
|
-
|
|
149
|
+
min-width: 72px;
|
|
150
|
+
padding: 8px 10px;
|
|
151
|
+
font-size: 0.72rem;
|
|
150
152
|
cursor: pointer;
|
|
151
|
-
transition: transform 0.15s, filter 0.15s;
|
|
153
|
+
transition: transform 0.15s, filter 0.15s, box-shadow 0.15s;
|
|
152
154
|
overflow: hidden;
|
|
153
155
|
text-overflow: ellipsis;
|
|
154
156
|
white-space: nowrap;
|
|
155
157
|
}
|
|
156
|
-
.treemap-cell:hover { transform: scale(1.
|
|
158
|
+
.treemap-cell:hover { transform: scale(1.04); filter: brightness(1.1); box-shadow: 0 4px 12px rgba(0,0,0,0.2); }
|
|
157
159
|
.treemap-cell[data-severity="critical"] { background: linear-gradient(180deg, #ef4444 0%, #b91c1c 100%); color: #fff; }
|
|
158
160
|
.treemap-cell[data-severity="high"] { background: linear-gradient(180deg, #f59e0b 0%, #d97706 100%); color: #fff; }
|
|
159
161
|
.treemap-cell[data-severity="medium"] { background: linear-gradient(180deg, #eab308 0%, #ca8a04 100%); color: #1a1a1f; }
|
|
160
162
|
.treemap-cell[data-severity="low"] { background: linear-gradient(180deg, #22c55e 0%, #16a34a 100%); color: #fff; }
|
|
161
163
|
.treemap-cell[data-severity="none"] { background: var(--border); color: var(--text-muted); }
|
|
162
|
-
#trendChart { max-height: 220px; }
|
|
163
164
|
.debt-list { list-style: none; padding: 0; margin: 0; }
|
|
164
165
|
.debt-list li {
|
|
165
166
|
border-bottom: 1px solid var(--border);
|
|
@@ -206,23 +207,15 @@ function buildHtml(run, title, darkMode) {
|
|
|
206
207
|
#detail .panel .file-assessment strong { color: var(--text); font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.05em; }
|
|
207
208
|
#detail .panel .suggested-code { margin-top: 1rem; padding: 0.75rem; background: var(--bg); border: 1px solid var(--border); border-radius: 8px; font-size: 0.8rem; overflow-x: auto; }
|
|
208
209
|
#detail .panel .suggested-code pre { margin: 0; white-space: pre-wrap; }
|
|
209
|
-
.llm-overall
|
|
210
|
+
.llm-overall { border-left: 4px solid var(--accent); }
|
|
211
|
+
.llm-overall h2 { color: var(--accent); }
|
|
212
|
+
.llm-overall .llm-overall-text { margin: 0; color: var(--text); line-height: 1.6; font-size: 1rem; }
|
|
213
|
+
.llm-next-steps h2 { color: var(--accent); }
|
|
214
|
+
.llm-next-steps ul { margin: 0; padding-left: 1.25rem; color: var(--text); line-height: 1.6; }
|
|
210
215
|
.priority-matrix { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin-top: 1rem; }
|
|
211
216
|
.priority-matrix .quadrant { padding: 1rem; border-radius: 8px; border: 1px solid var(--border); }
|
|
212
217
|
.priority-matrix .quadrant h4 { margin: 0 0 0.5rem; font-size: 0.9rem; }
|
|
213
218
|
.priority-matrix .quadrant p { margin: 0; font-size: 0.8rem; color: var(--text-muted); }
|
|
214
|
-
.glossary {
|
|
215
|
-
background: var(--surface);
|
|
216
|
-
border: 1px solid var(--border);
|
|
217
|
-
border-radius: 12px;
|
|
218
|
-
padding: 1.25rem 1.5rem;
|
|
219
|
-
margin-bottom: 1.5rem;
|
|
220
|
-
}
|
|
221
|
-
.glossary h2 { font-size: 0.95rem; margin: 0 0 0.75rem; color: var(--text-muted); font-weight: 600; }
|
|
222
|
-
.glossary dl { margin: 0; font-size: 0.875rem; line-height: 1.6; }
|
|
223
|
-
.glossary dt { font-weight: 600; color: var(--text); margin-top: 0.5rem; }
|
|
224
|
-
.glossary dt:first-child { margin-top: 0; }
|
|
225
|
-
.glossary dd { margin: 0.2rem 0 0 1rem; color: var(--text-muted); }
|
|
226
219
|
.section-desc { font-size: 0.875rem; color: var(--text-muted); margin: -0.5rem 0 1rem; line-height: 1.45; }
|
|
227
220
|
.legend { display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; margin-bottom: 1rem; font-size: 0.8rem; color: var(--text-muted); }
|
|
228
221
|
.legend span { display: inline-flex; align-items: center; gap: 0.35rem; }
|
|
@@ -235,18 +228,21 @@ function buildHtml(run, title, darkMode) {
|
|
|
235
228
|
</style>
|
|
236
229
|
</head>
|
|
237
230
|
<body>
|
|
231
|
+
${!hasLlm ? `
|
|
232
|
+
<div class="no-llm-banner">
|
|
233
|
+
<p class="no-llm-cta">Analysis run without LLM — for better results, run with LLM</p>
|
|
234
|
+
</div>
|
|
235
|
+
` : ""}
|
|
238
236
|
<div class="container">
|
|
239
237
|
<p class="hero-caption">Technical Debt Cleanliness Score</p>
|
|
240
238
|
<div class="hero tier-${cleanliness.tier}">
|
|
241
|
-
<div class="
|
|
239
|
+
<div class="score-badge">
|
|
242
240
|
<span class="num">${cleanliness.tier}</span>
|
|
243
241
|
<span class="of">of 5</span>
|
|
244
242
|
</div>
|
|
245
|
-
<div class="
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
<p class="report-meta">${escapeHtml(run.repoPath)} · ${run.completedAt ?? run.startedAt}</p>
|
|
249
|
-
</div>
|
|
243
|
+
<div class="score-label">${escapeHtml(cleanliness.label)}</div>
|
|
244
|
+
<p class="score-desc">${escapeHtml(cleanliness.description)}</p>
|
|
245
|
+
<p class="report-meta">${escapeHtml(run.repoPath)} · ${run.completedAt ?? run.startedAt}</p>
|
|
250
246
|
</div>
|
|
251
247
|
|
|
252
248
|
<div class="summary-cards">
|
|
@@ -271,25 +267,9 @@ function buildHtml(run, title, darkMode) {
|
|
|
271
267
|
</div>
|
|
272
268
|
` : ""}
|
|
273
269
|
|
|
274
|
-
<div class="glossary">
|
|
275
|
-
<h2>Understanding this report</h2>
|
|
276
|
-
<dl>
|
|
277
|
-
<dt>Debt score (0–100)</dt>
|
|
278
|
-
<dd>Combined severity and confidence of all issues. Lower is better. <40 = healthy, 40–70 = address soon, 70+ = high priority.</dd>
|
|
279
|
-
<dt>Severity</dt>
|
|
280
|
-
<dd><strong>Critical</strong> = fix first. <strong>High</strong> = plan soon. <strong>Medium/Low</strong> = backlog.</dd>
|
|
281
|
-
<dt>Cyclomatic complexity</dt>
|
|
282
|
-
<dd>Decision paths in code (if/else, loops). >10 high, >20 critical.</dd>
|
|
283
|
-
<dt>Hotspot</dt>
|
|
284
|
-
<dd>Files that change often and have high complexity—highest refactor risk.</dd>
|
|
285
|
-
<dt>Trend chart</dt>
|
|
286
|
-
<dd>Heuristic from recent commits; shows churn pattern, not full history.</dd>
|
|
287
|
-
</dl>
|
|
288
|
-
</div>
|
|
289
|
-
|
|
290
270
|
<div class="section">
|
|
291
|
-
<h2>
|
|
292
|
-
<p class="section-desc">
|
|
271
|
+
<h2>Files by debt</h2>
|
|
272
|
+
<p class="section-desc">Size = complexity + churn. Color = worst severity. Click for details.</p>
|
|
293
273
|
<div class="legend">
|
|
294
274
|
<span><span class="swatch swatch-crit"></span> Critical</span>
|
|
295
275
|
<span><span class="swatch swatch-high"></span> High</span>
|
|
@@ -300,12 +280,6 @@ function buildHtml(run, title, darkMode) {
|
|
|
300
280
|
<div id="treemap"></div>
|
|
301
281
|
</div>
|
|
302
282
|
|
|
303
|
-
<div class="section">
|
|
304
|
-
<h2>Debt trend (recent commits)</h2>
|
|
305
|
-
<p class="section-desc">Estimated activity per commit (files changed). Rising pattern may indicate growing churn; not a full historical debt metric.</p>
|
|
306
|
-
<canvas id="trendChart"></canvas>
|
|
307
|
-
</div>
|
|
308
|
-
|
|
309
283
|
<div class="section">
|
|
310
284
|
<h2>Prioritized recommendations</h2>
|
|
311
285
|
<p class="section-desc">Focus on high-impact items first. Easy wins = high severity but smaller files; harder = critical or hotspot files that need planning.</p>
|
|
@@ -355,38 +329,13 @@ function buildHtml(run, title, darkMode) {
|
|
|
355
329
|
cell.className = 'treemap-cell';
|
|
356
330
|
cell.dataset.severity = severity;
|
|
357
331
|
cell.style.flex = String(score / maxScore * 100) + ' 1 80px';
|
|
358
|
-
cell.style.minWidth = '
|
|
332
|
+
cell.style.minWidth = '72px';
|
|
359
333
|
cell.title = file;
|
|
360
334
|
cell.textContent = file.split('/').pop() || file;
|
|
361
335
|
cell.addEventListener('click', () => showDetail(file, items));
|
|
362
336
|
treemap.appendChild(cell);
|
|
363
337
|
});
|
|
364
338
|
|
|
365
|
-
// Trend chart
|
|
366
|
-
const ctx = document.getElementById('trendChart').getContext('2d');
|
|
367
|
-
new Chart(ctx, {
|
|
368
|
-
type: 'line',
|
|
369
|
-
data: {
|
|
370
|
-
labels: (DATA.debtTrend || []).map(t => t.commit),
|
|
371
|
-
datasets: [{
|
|
372
|
-
label: 'Debt score',
|
|
373
|
-
data: (DATA.debtTrend || []).map(t => t.score),
|
|
374
|
-
borderColor: '#6366f1',
|
|
375
|
-
backgroundColor: 'rgba(99, 102, 241, 0.1)',
|
|
376
|
-
fill: true,
|
|
377
|
-
tension: 0.3,
|
|
378
|
-
}]
|
|
379
|
-
},
|
|
380
|
-
options: {
|
|
381
|
-
responsive: true,
|
|
382
|
-
plugins: { legend: { display: false } },
|
|
383
|
-
scales: {
|
|
384
|
-
y: { beginAtZero: true, grid: { color: 'var(--border)' }, ticks: { color: 'var(--text-muted)' } },
|
|
385
|
-
x: { grid: { color: 'var(--border)' }, ticks: { color: 'var(--text-muted)' } }
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
});
|
|
389
|
-
|
|
390
339
|
// Priority quadrants
|
|
391
340
|
const highImpact = DATA.debtItems.filter(d => d.severity === 'high' || d.severity === 'critical').slice(0, 5);
|
|
392
341
|
document.getElementById('q1').innerHTML = highImpact.slice(0, 3).map(d => '<li style="font-size:0.8rem">' + escapeHtml(d.file) + '</li>').join('');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tech-debt-visualizer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
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",
|