git-archaeologist 1.0.0 → 1.2.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.
@@ -35,158 +35,211 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.generateHtmlReport = generateHtmlReport;
37
37
  const fs = __importStar(require("fs"));
38
- function escapeHtml(str) {
39
- return str
40
- .replace(/&/g, '&')
41
- .replace(/</g, '&lt;')
42
- .replace(/>/g, '&gt;')
43
- .replace(/"/g, '&quot;');
38
+ function esc(s) {
39
+ return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
44
40
  }
45
- function scoreColor(score) {
46
- if (score >= 800)
47
- return '#f87171';
48
- if (score >= 400)
49
- return '#fbbf24';
50
- if (score >= 100)
51
- return '#34d399';
52
- return '#6ee7b7';
41
+ function col(score, max) {
42
+ const r = Math.min(score / Math.max(max, 1), 1);
43
+ if (r > 0.7)
44
+ return '#ef4444';
45
+ if (r > 0.4)
46
+ return '#f97316';
47
+ if (r > 0.2)
48
+ return '#eab308';
49
+ if (r > 0.05)
50
+ return '#22c55e';
51
+ return '#3b82f6';
53
52
  }
54
- function busColor(n) {
55
- if (n === 1)
56
- return '#f87171';
57
- if (n === 2)
58
- return '#fbbf24';
59
- return '#34d399';
60
- }
61
- function pctColor(p) {
62
- if (p >= 90)
63
- return '#c084fc';
64
- if (p >= 70)
65
- return '#60a5fa';
66
- return '#94a3b8';
53
+ function buildTree(result) {
54
+ const sm = new Map();
55
+ for (const f of result.cursedFiles)
56
+ sm.set(f.filepath, f.curseScore);
57
+ const fm = new Map();
58
+ for (const [fp, stats] of result.fileStats) {
59
+ const parts = fp.split('/');
60
+ const folder = parts.length > 1 ? parts[0] : '(root)';
61
+ const score = sm.get(fp) ?? 0;
62
+ const last = new Date(stats.lastChanged * 1000).toISOString().split('T')[0];
63
+ if (!fm.has(folder))
64
+ fm.set(folder, []);
65
+ fm.get(folder).push({ name: parts[parts.length - 1], filepath: fp, value: Math.max(stats.totalChanges, 1), score, changes: stats.totalChanges, authors: stats.uniqueAuthors.size, lastTouched: last });
66
+ }
67
+ return { name: result.repoName, children: Array.from(fm.entries()).map(([f, files]) => ({ name: f, children: files })) };
67
68
  }
68
69
  function generateHtmlReport(result, outputPath) {
69
70
  const from = result.dateRange.from.toISOString().split('T')[0];
70
71
  const to = result.dateRange.to.toISOString().split('T')[0];
71
- const cursedRows = result.cursedFiles.slice(0, 20).map((f, i) => `
72
- <tr>
73
- <td class="rank">${i + 1}</td>
74
- <td class="filepath" title="${escapeHtml(f.filepath)}">${escapeHtml(f.filepath)}</td>
75
- <td><span class="badge" style="background:${scoreColor(f.curseScore)}">${f.curseScore.toFixed(1)}</span></td>
76
- <td>${f.totalChanges}</td>
77
- <td>${f.uniqueAuthors}</td>
78
- <td class="muted">${escapeHtml(f.reasons.join(', '))}</td>
79
- </tr>`).join('');
80
- const busRows = result.busFactor.map((b) => `
81
- <tr>
82
- <td>${escapeHtml(b.scope)}</td>
83
- <td><span class="badge" style="background:${busColor(b.busFactor)}">${b.busFactor}</span></td>
84
- <td>${b.filesAtRisk}</td>
85
- <td>${escapeHtml(b.atRiskAuthors.slice(0, 3).join(', '))}</td>
86
- <td class="muted">${escapeHtml(b.warning.replace(/[⚠️⚡✓]/gu, ''))}</td>
87
- </tr>`).join('');
88
- const ownerRows = result.ownership
89
- .filter((o) => o.ownershipPercent >= 60)
90
- .slice(0, 20)
91
- .map((o) => `
92
- <tr>
93
- <td class="filepath" title="${escapeHtml(o.filepath)}">${escapeHtml(o.filepath)}</td>
94
- <td>${escapeHtml(o.owner)}</td>
95
- <td><span class="badge" style="background:${pctColor(o.ownershipPercent)}">${o.ownershipPercent}%</span></td>
96
- <td class="muted">${o.contributors.slice(1, 3).map(c => escapeHtml(`${c.name} (${c.percent}%)`)).join(', ') || '—'}</td>
97
- </tr>`).join('');
98
- const couplingRows = result.coupling.slice(0, 15).map((c) => `
99
- <tr>
100
- <td class="filepath" title="${escapeHtml(c.fileA)}">${escapeHtml(c.fileA)}</td>
101
- <td class="filepath" title="${escapeHtml(c.fileB)}">${escapeHtml(c.fileB)}</td>
102
- <td>${c.coChanges}</td>
103
- <td><span class="badge" style="background:${scoreColor(c.couplingScore * 8)}">${c.couplingScore}%</span></td>
104
- </tr>`).join('');
72
+ const maxS = result.cursedFiles[0]?.curseScore ?? 1;
73
+ const treeJSON = JSON.stringify(buildTree(result));
74
+ const cursedRows = result.cursedFiles.slice(0, 20).map((f, i) => {
75
+ const c = col(f.curseScore, maxS);
76
+ const stats = result.fileStats.get(f.filepath);
77
+ const last = stats ? new Date(stats.lastChanged * 1000).toISOString().split('T')[0] : '—';
78
+ return `<tr onclick="hl('${esc(f.filepath)}')" style="cursor:pointer">
79
+ <td style="color:#64748b;width:32px">${i + 1}</td>
80
+ <td style="font-family:monospace;font-size:12px">${esc(f.filepath)}</td>
81
+ <td><span style="background:${c};color:#000;padding:2px 10px;border-radius:20px;font-size:12px;font-weight:700">${f.curseScore.toFixed(0)}</span></td>
82
+ <td style="color:#94a3b8">${f.totalChanges}</td>
83
+ <td style="color:#94a3b8">${f.uniqueAuthors}</td>
84
+ <td style="color:#64748b;font-size:12px">${last}</td>
85
+ </tr>`;
86
+ }).join('');
87
+ const busRows = result.busFactor.map(b => {
88
+ const bc = b.busFactor === 1 ? '#ef4444' : b.busFactor === 2 ? '#f97316' : '#22c55e';
89
+ const icon = b.busFactor === 1 ? '⚠' : b.busFactor === 2 ? '⚡' : '✓';
90
+ return `<tr>
91
+ <td style="font-family:monospace;font-size:12px">${esc(b.scope)}</td>
92
+ <td><span style="background:${bc};color:#000;padding:2px 10px;border-radius:20px;font-weight:700">${icon} ${b.busFactor}</span></td>
93
+ <td style="color:#94a3b8">${b.filesAtRisk}</td>
94
+ <td style="color:#94a3b8;font-size:12px">${esc(b.atRiskAuthors.slice(0, 2).join(', '))}</td>
95
+ </tr>`;
96
+ }).join('');
97
+ const couplingRows = result.coupling.slice(0, 15).map(c2 => {
98
+ const cc = c2.couplingScore >= 80 ? '#ef4444' : c2.couplingScore >= 50 ? '#f97316' : '#eab308';
99
+ return `<tr>
100
+ <td style="font-family:monospace;font-size:11px">${esc(c2.fileA)}</td>
101
+ <td style="color:#475569;text-align:center;padding:0 8px">↔</td>
102
+ <td style="font-family:monospace;font-size:11px">${esc(c2.fileB)}</td>
103
+ <td><span style="background:${cc};color:#000;padding:2px 10px;border-radius:20px;font-weight:600">${c2.couplingScore}%</span></td>
104
+ </tr>`;
105
+ }).join('');
105
106
  const html = `<!DOCTYPE html>
106
107
  <html lang="en">
107
108
  <head>
108
- <meta charset="UTF-8"/>
109
- <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
110
- <title>⛏ Git Archaeologist — ${escapeHtml(result.repoName)}</title>
111
- <style>
112
- *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
113
- :root {
114
- --bg: #0f1117; --surface: #1a1d27; --border: #2a2d3a;
115
- --text: #e2e8f0; --muted: #64748b; --accent: #a78bfa;
116
- }
117
- body { background: var(--bg); color: var(--text); font-family: 'Inter', system-ui, sans-serif; font-size: 14px; line-height: 1.6; }
118
- header { background: var(--surface); border-bottom: 1px solid var(--border); padding: 24px 40px; display: flex; align-items: center; gap: 16px; }
119
- header h1 { font-size: 20px; font-weight: 600; color: var(--accent); }
120
- header p { color: var(--muted); font-size: 13px; }
121
- .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 16px; padding: 32px 40px 0; }
122
- .stat-card { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 16px 20px; }
123
- .stat-card .label { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; margin-bottom: 6px; }
124
- .stat-card .value { font-size: 28px; font-weight: 700; color: var(--accent); }
125
- .stat-card .sub { font-size: 12px; color: var(--muted); margin-top: 2px; }
126
- main { padding: 32px 40px; display: flex; flex-direction: column; gap: 40px; }
127
- section h2 { font-size: 15px; font-weight: 600; color: var(--accent); margin-bottom: 16px; display: flex; align-items: center; gap: 8px; }
128
- table { width: 100%; border-collapse: collapse; background: var(--surface); border-radius: 10px; overflow: hidden; border: 1px solid var(--border); }
129
- th { background: #12151f; color: var(--muted); font-size: 12px; text-transform: uppercase; letter-spacing: .05em; padding: 10px 14px; text-align: left; font-weight: 500; }
130
- td { padding: 10px 14px; border-top: 1px solid var(--border); vertical-align: middle; }
131
- tr:hover td { background: #1e2130; }
132
- .rank { color: var(--muted); font-size: 12px; width: 40px; }
133
- .filepath { font-family: 'JetBrains Mono', 'Fira Code', monospace; font-size: 12px; max-width: 300px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
134
- .muted { color: var(--muted); font-size: 12px; }
135
- .badge { display: inline-block; padding: 2px 10px; border-radius: 20px; font-size: 12px; font-weight: 600; color: #0f1117; }
136
- footer { text-align: center; padding: 32px; color: var(--muted); font-size: 12px; border-top: 1px solid var(--border); }
137
- </style>
109
+ <meta charset="UTF-8"/>
110
+ <meta name="viewport" content="width=device-width,initial-scale=1"/>
111
+ <title>⛏ ${esc(result.repoName)} — Git Archaeologist</title>
112
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"><\/script>
113
+ <style>
114
+ *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
115
+ :root{--bg:#0a0d14;--s1:#111520;--s2:#161b28;--bd:#1e2535;--tx:#e2e8f0;--mu:#64748b;--ac:#a78bfa}
116
+ body{background:var(--bg);color:var(--tx);font-family:Inter,system-ui,sans-serif;font-size:14px;line-height:1.6}
117
+ header{background:var(--s1);border-bottom:1px solid var(--bd);padding:18px 32px;display:flex;align-items:center;justify-content:space-between;position:sticky;top:0;z-index:100}
118
+ header h1{font-size:18px;font-weight:700;color:var(--ac)}
119
+ header p{font-size:12px;color:var(--mu);margin-top:2px}
120
+ .legend{font-size:12px;color:var(--mu);display:flex;gap:12px;align-items:center}
121
+ .legend span{display:flex;align-items:center;gap:4px}
122
+ .dot{width:10px;height:10px;border-radius:50%;display:inline-block}
123
+ .stats{display:grid;grid-template-columns:repeat(6,1fr);gap:12px;padding:24px 32px 0}
124
+ .stat{background:var(--s1);border:1px solid var(--bd);border-radius:12px;padding:16px;text-align:center}
125
+ .stat .n{font-size:30px;font-weight:800;color:var(--ac);letter-spacing:-0.03em}
126
+ .stat .l{font-size:11px;color:var(--mu);text-transform:uppercase;letter-spacing:.06em;margin-top:4px}
127
+ .stat.d .n{color:#ef4444}
128
+ .tm-wrap{padding:20px 32px}
129
+ .tm-wrap h2{font-size:12px;font-weight:600;color:var(--mu);text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px}
130
+ #tm{width:100%;height:500px;background:var(--s1);border:1px solid var(--bd);border-radius:12px;overflow:hidden}
131
+ #tip{position:fixed;background:rgba(10,13,20,.97);border:1px solid var(--bd);border-radius:10px;padding:12px 16px;pointer-events:none;font-size:12px;z-index:999;max-width:300px;display:none;box-shadow:0 8px 32px rgba(0,0,0,.5)}
132
+ .tn{font-family:monospace;font-weight:600;color:var(--ac);margin-bottom:8px;word-break:break-all;font-size:11px}
133
+ .tr{display:flex;justify-content:space-between;gap:20px;color:var(--mu);margin-top:3px}
134
+ .tr span:last-child{color:var(--tx);font-weight:500}
135
+ .tables{display:grid;grid-template-columns:1fr 1fr;gap:16px;padding:20px 32px 32px}
136
+ .full{grid-column:1/-1}
137
+ .card{background:var(--s1);border:1px solid var(--bd);border-radius:12px;overflow:hidden}
138
+ .ch{padding:14px 20px;border-bottom:1px solid var(--bd);font-size:13px;font-weight:600;color:var(--ac)}
139
+ table{width:100%;border-collapse:collapse}
140
+ th{padding:9px 20px;text-align:left;font-size:11px;font-weight:500;color:var(--mu);text-transform:uppercase;letter-spacing:.06em;background:var(--s2)}
141
+ td{padding:10px 20px;border-top:1px solid var(--bd);vertical-align:middle}
142
+ tr:hover td{background:rgba(167,139,250,.04)}
143
+ footer{text-align:center;padding:24px;color:var(--mu);font-size:12px;border-top:1px solid var(--bd)}
144
+ footer a{color:var(--ac);text-decoration:none}
145
+ @media(max-width:900px){.stats{grid-template-columns:repeat(3,1fr)}.tables{grid-template-columns:1fr}}
146
+ </style>
138
147
  </head>
139
148
  <body>
140
- <header>
141
- <div>
142
- <h1>⛏ Git Archaeologist</h1>
143
- <p>${escapeHtml(result.repoName)} &nbsp;·&nbsp; ${result.totalCommits.toLocaleString()} commits &nbsp;·&nbsp; ${from} → ${to} &nbsp;·&nbsp; analyzed ${result.analyzedAt.toLocaleString()}</p>
144
- </div>
145
- </header>
146
-
147
- <div class="stats-grid">
148
- <div class="stat-card"><div class="label">Total Commits</div><div class="value">${result.totalCommits.toLocaleString()}</div></div>
149
- <div class="stat-card"><div class="label">Files Tracked</div><div class="value">${result.totalFiles.toLocaleString()}</div></div>
150
- <div class="stat-card"><div class="label">Contributors</div><div class="value">${result.totalAuthors.toLocaleString()}</div></div>
151
- <div class="stat-card"><div class="label">Cursed Files</div><div class="value" style="color:#f87171">${result.cursedFiles.length}</div><div class="sub">top instability</div></div>
152
- <div class="stat-card"><div class="label">Bus Factor 1</div><div class="value" style="color:#f87171">${result.busFactor.filter(b => b.busFactor === 1).length}</div><div class="sub">single-owner modules</div></div>
153
- <div class="stat-card"><div class="label">Coupled Pairs</div><div class="value" style="color:#fbbf24">${result.coupling.length}</div><div class="sub">implicit links</div></div>
149
+ <header>
150
+ <div>
151
+ <h1>⛏ Git Archaeologist</h1>
152
+ <p>${esc(result.repoName)} · ${result.totalCommits.toLocaleString()} commits · ${from} → ${to} · analyzed ${result.analyzedAt.toLocaleString()}</p>
153
+ </div>
154
+ <div class="legend">
155
+ <span><i class="dot" style="background:#ef4444"></i>Critical</span>
156
+ <span><i class="dot" style="background:#f97316"></i>High</span>
157
+ <span><i class="dot" style="background:#eab308"></i>Medium</span>
158
+ <span><i class="dot" style="background:#22c55e"></i>Low</span>
159
+ <span><i class="dot" style="background:#3b82f6"></i>Safe</span>
154
160
  </div>
155
-
156
- <main>
157
- <section>
158
- <h2>💀 Cursed Files — highest instability score</h2>
159
- <table>
160
- <thead><tr><th>#</th><th>File</th><th>Score</th><th>Changes</th><th>Authors</th><th>Why</th></tr></thead>
161
- <tbody>${cursedRows}</tbody>
162
- </table>
163
- </section>
164
-
165
- <section>
166
- <h2>🚌 Bus Factor — single points of failure</h2>
167
- <table>
168
- <thead><tr><th>Module</th><th>Bus Factor</th><th>Files</th><th>Key People</th><th>Risk</th></tr></thead>
169
- <tbody>${busRows}</tbody>
170
- </table>
171
- </section>
172
-
173
- <section>
174
- <h2>👑 Ownership — who truly owns each file</h2>
175
- <table>
176
- <thead><tr><th>File</th><th>Owner</th><th>Ownership</th><th>Other Contributors</th></tr></thead>
177
- <tbody>${ownerRows}</tbody>
178
- </table>
179
- </section>
180
-
181
- <section>
182
- <h2>🔗 Implicit Coupling — files that always change together</h2>
183
- <table>
184
- <thead><tr><th>File A</th><th>File B</th><th>Co-changes</th><th>Coupling</th></tr></thead>
185
- <tbody>${couplingRows}</tbody>
186
- </table>
187
- </section>
188
- </main>
189
- <footer>Generated by ⛏ Git Archaeologist</footer>
161
+ </header>
162
+ <div class="stats">
163
+ <div class="stat"><div class="n">${result.totalCommits.toLocaleString()}</div><div class="l">Commits</div></div>
164
+ <div class="stat"><div class="n">${result.totalFiles.toLocaleString()}</div><div class="l">Files</div></div>
165
+ <div class="stat"><div class="n">${result.totalAuthors.toLocaleString()}</div><div class="l">Authors</div></div>
166
+ <div class="stat d"><div class="n">${result.cursedFiles.length}</div><div class="l">Cursed Files</div></div>
167
+ <div class="stat d"><div class="n">${result.busFactor.filter((b) => b.busFactor === 1).length}</div><div class="l">Bus Factor 1</div></div>
168
+ <div class="stat d"><div class="n">${result.coupling.length}</div><div class="l">Coupled Pairs</div></div>
169
+ </div>
170
+ <div class="tm-wrap">
171
+ <h2>🗺 Codebase Risk Map — size = commit frequency · color = curse score</h2>
172
+ <div id="tm"></div>
173
+ </div>
174
+ <div id="tip">
175
+ <div class="tn" id="tt-fp"></div>
176
+ <div class="tr"><span>Curse score</span><span id="tt-sc"></span></div>
177
+ <div class="tr"><span>Changes</span><span id="tt-ch"></span></div>
178
+ <div class="tr"><span>Authors</span><span id="tt-au"></span></div>
179
+ <div class="tr"><span>Last touched</span><span id="tt-lt"></span></div>
180
+ </div>
181
+ <div class="tables">
182
+ <div class="card full"><div class="ch">💀 Cursed Files — highest instability score</div>
183
+ <table><thead><tr><th>#</th><th>File</th><th>Score</th><th>Changes</th><th>Authors</th><th>Last touched</th></tr></thead>
184
+ <tbody>${cursedRows}</tbody></table></div>
185
+ <div class="card"><div class="ch">🚌 Bus Factor — single points of failure</div>
186
+ <table><thead><tr><th>Module</th><th>Factor</th><th>Files</th><th>Key people</th></tr></thead>
187
+ <tbody>${busRows}</tbody></table></div>
188
+ <div class="card"><div class="ch">🔗 Implicit Coupling</div>
189
+ <table><thead><tr><th>File A</th><th></th><th>File B</th><th>Score</th></tr></thead>
190
+ <tbody>${couplingRows}</tbody></table></div>
191
+ </div>
192
+ <footer>Generated by <a href="https://github.com/SushantVerma7969/git-archaeologist">⛏ git-archaeologist</a> · <a href="https://www.npmjs.com/package/git-archaeologist">npm</a></footer>
193
+ <script>
194
+ const DATA=${treeJSON};
195
+ const MAX=${maxS};
196
+ function sc(s){const r=Math.min(s/Math.max(MAX,1),1);if(r>.7)return"#ef4444";if(r>.4)return"#f97316";if(r>.2)return"#eab308";if(r>.05)return"#22c55e";return"#3b82f6";}
197
+ function dk(h,a){const n=parseInt(h.slice(1),16);return"#"+[Math.max(0,(n>>16)-a),Math.max(0,((n>>8)&255)-a),Math.max(0,(n&255)-a)].map(v=>v.toString(16).padStart(2,"0")).join("");}
198
+ const el=document.getElementById("tm");
199
+ const W=el.clientWidth,H=el.clientHeight;
200
+ const svg=d3.select("#tm").append("svg").attr("width",W).attr("height",H);
201
+ const root=d3.hierarchy(DATA).sum(d=>d.value||0).sort((a,b)=>(b.value||0)-(a.value||0));
202
+ d3.treemap().size([W,H]).padding(2).paddingTop(22).round(true)(root);
203
+ const tip=document.getElementById("tip");
204
+ function show(e,d){
205
+ if(!d.data.filepath)return;
206
+ document.getElementById("tt-fp").textContent=d.data.filepath;
207
+ document.getElementById("tt-sc").textContent=d.data.score?d.data.score.toFixed(1):"—";
208
+ document.getElementById("tt-ch").textContent=d.data.changes||"—";
209
+ document.getElementById("tt-au").textContent=d.data.authors||"—";
210
+ document.getElementById("tt-lt").textContent=d.data.lastTouched||"—";
211
+ tip.style.display="block";mv(e);
212
+ }
213
+ function mv(e){
214
+ const x=e.clientX+16,y=e.clientY-10;
215
+ const tw=tip.offsetWidth,th=tip.offsetHeight;
216
+ tip.style.left=(x+tw>window.innerWidth?x-tw-32:x)+"px";
217
+ tip.style.top=(y+th>window.innerHeight?y-th:y)+"px";
218
+ }
219
+ svg.selectAll("g.fd").data(root.children||[]).join("g").attr("class","fd").call(g=>{
220
+ g.append("rect").attr("x",d=>d.x0).attr("y",d=>d.y0).attr("width",d=>d.x1-d.x0).attr("height",d=>d.y1-d.y0).attr("fill","none").attr("stroke","#1e2535").attr("stroke-width",1);
221
+ g.append("text").attr("x",d=>d.x0+6).attr("y",d=>d.y0+15).attr("fill","#64748b").attr("font-size","11px").attr("font-weight","600").attr("font-family","monospace").text(d=>d.data.name);
222
+ });
223
+ const lv=svg.selectAll("g.lf").data(root.leaves()).join("g").attr("class","lf");
224
+ lv.append("rect")
225
+ .attr("x",d=>d.x0+1).attr("y",d=>d.y0+1)
226
+ .attr("width",d=>Math.max(0,d.x1-d.x0-2)).attr("height",d=>Math.max(0,d.y1-d.y0-2))
227
+ .attr("rx",3).attr("fill",d=>sc(d.data.score||0)).attr("fill-opacity",.82)
228
+ .attr("stroke",d=>dk(sc(d.data.score||0),40)).attr("stroke-width",.5)
229
+ .style("cursor","pointer")
230
+ .on("mouseenter",function(e,d){d3.select(this).attr("fill-opacity",1).attr("stroke-width",2);show(e,d);})
231
+ .on("mousemove",mv)
232
+ .on("mouseleave",function(){d3.select(this).attr("fill-opacity",.82).attr("stroke-width",.5);tip.style.display="none";});
233
+ lv.filter(d=>(d.x1-d.x0)>55&&(d.y1-d.y0)>22).append("text")
234
+ .attr("x",d=>d.x0+5).attr("y",d=>d.y0+14)
235
+ .attr("fill","rgba(0,0,0,.85)").attr("font-size","10px").attr("font-family","monospace").attr("pointer-events","none")
236
+ .text(d=>{const nm=d.data.name;const mx=Math.floor((d.x1-d.x0-10)/6);return nm.length>mx?nm.slice(0,mx-1)+"…":nm;});
237
+ window.hl=function(fp){
238
+ lv.selectAll("rect").attr("stroke-width",d=>d.data.filepath===fp?3:.5).attr("stroke",d=>d.data.filepath===fp?"#fff":dk(sc(d.data.score||0),40)).attr("fill-opacity",d=>d.data.filepath===fp?1:.82);
239
+ const node=root.leaves().find(d=>d.data.filepath===fp);
240
+ if(node){const cx=(node.x0+node.x1)/2,cy=(node.y0+node.y1)/2;svg.transition().duration(400);}
241
+ };
242
+ <\/script>
190
243
  </body>
191
244
  </html>`;
192
245
  fs.writeFileSync(outputPath, html, 'utf8');
@@ -1 +1 @@
1
- {"version":3,"file":"htmlReport.js","sourceRoot":"","sources":["../../src/output/htmlReport.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"htmlReport.js","sourceRoot":"","sources":["../../src/output/htmlReport.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "git-archaeologist",
3
- "version": "1.0.0",
4
- "description": "Uncover the hidden history, ownership, and tech debt in any git repository",
3
+ "version": "1.2.0",
4
+ "description": "Find the most dangerous files in any git repo — curse scores, bus factor, hidden coupling",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
7
  "git-arch": "dist/index.js"
@@ -18,7 +18,7 @@
18
18
  "tech-debt",
19
19
  "analytics"
20
20
  ],
21
- "author": "sushantverma7969",
21
+ "author": "Sushant Verma",
22
22
  "license": "MIT",
23
23
  "type": "commonjs",
24
24
  "devDependencies": {
@@ -35,7 +35,7 @@
35
35
  "homepage": "https://github.com/SushantVerma7969/git-archaeologist",
36
36
  "repository": {
37
37
  "type": "git",
38
- "url": "https://github.com/SushantVerma7969/git-archaeologist.git"
38
+ "url": "git+https://github.com/SushantVerma7969/git-archaeologist.git"
39
39
  },
40
40
  "bugs": {
41
41
  "url": "https://github.com/SushantVerma7969/git-archaeologist/issues"
@@ -43,4 +43,4 @@
43
43
  "engines": {
44
44
  "node": ">=18"
45
45
  }
46
- }
46
+ }
package/preview.png ADDED
Binary file
package/demo.gif DELETED
Binary file