@girardelli/architect 1.3.0 → 2.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.
Files changed (58) hide show
  1. package/README.md +111 -112
  2. package/dist/agent-generator.d.ts +106 -0
  3. package/dist/agent-generator.d.ts.map +1 -0
  4. package/dist/agent-generator.js +1398 -0
  5. package/dist/agent-generator.js.map +1 -0
  6. package/dist/cli.js +132 -15
  7. package/dist/cli.js.map +1 -1
  8. package/dist/html-reporter.d.ts +8 -2
  9. package/dist/html-reporter.d.ts.map +1 -1
  10. package/dist/html-reporter.js +773 -50
  11. package/dist/html-reporter.js.map +1 -1
  12. package/dist/index.d.ts +26 -2
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +25 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/refactor-engine.d.ts +18 -0
  17. package/dist/refactor-engine.d.ts.map +1 -0
  18. package/dist/refactor-engine.js +86 -0
  19. package/dist/refactor-engine.js.map +1 -0
  20. package/dist/refactor-reporter.d.ts +20 -0
  21. package/dist/refactor-reporter.d.ts.map +1 -0
  22. package/dist/refactor-reporter.js +389 -0
  23. package/dist/refactor-reporter.js.map +1 -0
  24. package/dist/rules/barrel-optimizer.d.ts +13 -0
  25. package/dist/rules/barrel-optimizer.d.ts.map +1 -0
  26. package/dist/rules/barrel-optimizer.js +77 -0
  27. package/dist/rules/barrel-optimizer.js.map +1 -0
  28. package/dist/rules/dead-code-detector.d.ts +21 -0
  29. package/dist/rules/dead-code-detector.d.ts.map +1 -0
  30. package/dist/rules/dead-code-detector.js +117 -0
  31. package/dist/rules/dead-code-detector.js.map +1 -0
  32. package/dist/rules/hub-splitter.d.ts +13 -0
  33. package/dist/rules/hub-splitter.d.ts.map +1 -0
  34. package/dist/rules/hub-splitter.js +110 -0
  35. package/dist/rules/hub-splitter.js.map +1 -0
  36. package/dist/rules/import-organizer.d.ts +13 -0
  37. package/dist/rules/import-organizer.d.ts.map +1 -0
  38. package/dist/rules/import-organizer.js +85 -0
  39. package/dist/rules/import-organizer.js.map +1 -0
  40. package/dist/rules/module-grouper.d.ts +13 -0
  41. package/dist/rules/module-grouper.d.ts.map +1 -0
  42. package/dist/rules/module-grouper.js +110 -0
  43. package/dist/rules/module-grouper.js.map +1 -0
  44. package/dist/types.d.ts +51 -0
  45. package/dist/types.d.ts.map +1 -1
  46. package/package.json +1 -1
  47. package/src/agent-generator.ts +1526 -0
  48. package/src/cli.ts +150 -15
  49. package/src/html-reporter.ts +799 -51
  50. package/src/index.ts +39 -1
  51. package/src/refactor-engine.ts +117 -0
  52. package/src/refactor-reporter.ts +408 -0
  53. package/src/rules/barrel-optimizer.ts +97 -0
  54. package/src/rules/dead-code-detector.ts +132 -0
  55. package/src/rules/hub-splitter.ts +123 -0
  56. package/src/rules/import-organizer.ts +98 -0
  57. package/src/rules/module-grouper.ts +124 -0
  58. package/src/types.ts +52 -0
@@ -0,0 +1,389 @@
1
+ /**
2
+ * Generates interactive HTML report for refactoring plans.
3
+ * Includes step-by-step roadmap, score predictions, and file operation previews.
4
+ */
5
+ export class RefactorReportGenerator {
6
+ generateHtml(plan) {
7
+ return `<!DOCTYPE html>
8
+ <html lang="en">
9
+ <head>
10
+ <meta charset="UTF-8">
11
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
12
+ <title>Architect Refactoring Plan</title>
13
+ ${this.getStyles()}
14
+ </head>
15
+ <body>
16
+ ${this.renderHeader(plan)}
17
+ <div class="container">
18
+ ${this.renderScoreComparison(plan)}
19
+ ${this.renderSummaryStats(plan)}
20
+ ${this.renderRoadmap(plan)}
21
+ </div>
22
+ ${this.renderFooter()}
23
+ </body>
24
+ </html>`;
25
+ }
26
+ scoreColor(score) {
27
+ if (score >= 70)
28
+ return '#22c55e';
29
+ if (score >= 50)
30
+ return '#f59e0b';
31
+ return '#ef4444';
32
+ }
33
+ escapeHtml(text) {
34
+ return text
35
+ .replace(/&/g, '&amp;')
36
+ .replace(/</g, '&lt;')
37
+ .replace(/>/g, '&gt;')
38
+ .replace(/"/g, '&quot;');
39
+ }
40
+ opColor(type) {
41
+ switch (type) {
42
+ case 'CREATE': return '#22c55e';
43
+ case 'MOVE': return '#3b82f6';
44
+ case 'MODIFY': return '#f59e0b';
45
+ case 'DELETE': return '#ef4444';
46
+ default: return '#64748b';
47
+ }
48
+ }
49
+ opIcon(type) {
50
+ switch (type) {
51
+ case 'CREATE': return '➕';
52
+ case 'MOVE': return '📦';
53
+ case 'MODIFY': return '✏️';
54
+ case 'DELETE': return '🗑️';
55
+ default: return '📄';
56
+ }
57
+ }
58
+ renderHeader(plan) {
59
+ const date = new Date(plan.timestamp).toLocaleDateString('en-US', {
60
+ year: 'numeric', month: 'long', day: 'numeric',
61
+ });
62
+ return `
63
+ <div class="header">
64
+ <h1>🔧 Architect — Refactoring Plan</h1>
65
+ <p class="subtitle">Actionable roadmap to improve your architecture</p>
66
+ <div class="meta">
67
+ <span>📂 <strong>${this.escapeHtml(plan.projectPath)}</strong></span>
68
+ <span>📋 <strong>${plan.steps.length}</strong> steps</span>
69
+ <span>🔄 <strong>${plan.totalOperations}</strong> operations</span>
70
+ <span>📅 <strong>${date}</strong></span>
71
+ </div>
72
+ </div>`;
73
+ }
74
+ renderScoreComparison(plan) {
75
+ const current = plan.currentScore;
76
+ const estimated = plan.estimatedScoreAfter;
77
+ const improvement = estimated.overall - current.overall;
78
+ const metrics = Object.keys(current.breakdown);
79
+ const bars = metrics.map(metric => {
80
+ const before = current.breakdown[metric];
81
+ const after = estimated.breakdown[metric];
82
+ const diff = after - before;
83
+ return `
84
+ <div class="comparison-row">
85
+ <div class="metric-name">${metric}</div>
86
+ <div class="metric-bars">
87
+ <div class="bar-before" style="width: ${before}%; background: ${this.scoreColor(before)}40">
88
+ <span>${before}</span>
89
+ </div>
90
+ <div class="bar-after" style="width: ${after}%; background: ${this.scoreColor(after)}">
91
+ <span>${after}</span>
92
+ </div>
93
+ </div>
94
+ <div class="metric-diff" style="color: ${diff > 0 ? '#22c55e' : '#64748b'}">
95
+ ${diff > 0 ? `+${diff}` : diff === 0 ? '—' : diff}
96
+ </div>
97
+ </div>`;
98
+ }).join('');
99
+ return `
100
+ <h2 class="section-title">📊 Score Prediction</h2>
101
+ <div class="card score-comparison">
102
+ <div class="score-pair">
103
+ <div class="score-box">
104
+ <div class="score-num" style="color: ${this.scoreColor(current.overall)}">${current.overall}</div>
105
+ <div class="score-label">Current</div>
106
+ </div>
107
+ <div class="score-arrow">
108
+ <svg width="60" height="30" viewBox="0 0 60 30">
109
+ <path d="M5 15 L45 15 M40 8 L48 15 L40 22" stroke="#818cf8" stroke-width="2.5" fill="none"/>
110
+ </svg>
111
+ </div>
112
+ <div class="score-box">
113
+ <div class="score-num" style="color: ${this.scoreColor(estimated.overall)}">${estimated.overall}</div>
114
+ <div class="score-label">Estimated</div>
115
+ </div>
116
+ <div class="score-diff" style="color: #22c55e">+${improvement} pts</div>
117
+ </div>
118
+ <div class="score-bars-section">
119
+ <div class="bars-legend">
120
+ <span class="legend-tag before">Before</span>
121
+ <span class="legend-tag after">After</span>
122
+ </div>
123
+ ${bars}
124
+ </div>
125
+ </div>`;
126
+ }
127
+ renderSummaryStats(plan) {
128
+ const criticalCount = plan.steps.filter(s => s.priority === 'CRITICAL').length;
129
+ const highCount = plan.steps.filter(s => s.priority === 'HIGH').length;
130
+ const mediumCount = plan.steps.filter(s => s.priority === 'MEDIUM').length;
131
+ const lowCount = plan.steps.filter(s => s.priority === 'LOW').length;
132
+ return `
133
+ <div class="stats-grid">
134
+ <div class="stat-card">
135
+ <div class="value">${plan.steps.length}</div>
136
+ <div class="label">Total Steps</div>
137
+ </div>
138
+ <div class="stat-card">
139
+ <div class="value">${plan.tier1Steps}</div>
140
+ <div class="label">Tier 1 (Rules)</div>
141
+ </div>
142
+ <div class="stat-card">
143
+ <div class="value">${plan.tier2Steps}</div>
144
+ <div class="label">Tier 2 (AST)</div>
145
+ </div>
146
+ <div class="stat-card">
147
+ <div class="value">${plan.totalOperations}</div>
148
+ <div class="label">File Operations</div>
149
+ </div>
150
+ </div>
151
+ <div class="priority-bar">
152
+ ${criticalCount ? `<div class="prio-seg prio-critical" style="flex: ${criticalCount}">🔴 ${criticalCount}</div>` : ''}
153
+ ${highCount ? `<div class="prio-seg prio-high" style="flex: ${highCount}">🟠 ${highCount}</div>` : ''}
154
+ ${mediumCount ? `<div class="prio-seg prio-medium" style="flex: ${mediumCount}">🔵 ${mediumCount}</div>` : ''}
155
+ ${lowCount ? `<div class="prio-seg prio-low" style="flex: ${lowCount}">🟢 ${lowCount}</div>` : ''}
156
+ </div>`;
157
+ }
158
+ renderRoadmap(plan) {
159
+ if (plan.steps.length === 0) {
160
+ return `
161
+ <h2 class="section-title">✅ No Refactoring Needed</h2>
162
+ <div class="card success-card">
163
+ <p>Your architecture is clean! No refactoring suggestions at this time.</p>
164
+ </div>`;
165
+ }
166
+ const stepsHtml = plan.steps.map(step => this.renderStep(step)).join('');
167
+ return `
168
+ <h2 class="section-title">🗺️ Refactoring Roadmap</h2>
169
+ <div class="roadmap">
170
+ ${stepsHtml}
171
+ </div>`;
172
+ }
173
+ renderStep(step) {
174
+ const operationsHtml = step.operations.map(op => `
175
+ <div class="operation">
176
+ <span class="op-icon">${this.opIcon(op.type)}</span>
177
+ <span class="op-badge" style="background: ${this.opColor(op.type)}20; color: ${this.opColor(op.type)}; border: 1px solid ${this.opColor(op.type)}40">${op.type}</span>
178
+ <code class="op-path">${this.escapeHtml(op.path)}</code>
179
+ ${op.newPath ? `<span class="op-arrow">→</span> <code class="op-path">${this.escapeHtml(op.newPath)}</code>` : ''}
180
+ <div class="op-desc">${this.escapeHtml(op.description)}</div>
181
+ </div>
182
+ `).join('');
183
+ const impactHtml = step.scoreImpact.map(i => `<span class="impact-tag">${i.metric}: ${i.before}→${i.after} <span class="impact-diff">+${i.after - i.before}</span></span>`).join('');
184
+ return `
185
+ <div class="step-card" data-priority="${step.priority}">
186
+ <div class="step-header">
187
+ <div class="step-number">${step.id}</div>
188
+ <div class="step-info">
189
+ <div class="step-title-row">
190
+ <h3>${this.escapeHtml(step.title)}</h3>
191
+ <span class="severity-badge severity-${step.priority}">${step.priority}</span>
192
+ <span class="tier-badge">Tier ${step.tier}</span>
193
+ </div>
194
+ <p class="step-desc">${this.escapeHtml(step.description)}</p>
195
+ <details class="step-details">
196
+ <summary>📖 Why?</summary>
197
+ <p class="rationale">${this.escapeHtml(step.rationale)}</p>
198
+ </details>
199
+ </div>
200
+ </div>
201
+ <div class="step-operations">
202
+ <h4>📋 Operations (${step.operations.length})</h4>
203
+ ${operationsHtml}
204
+ </div>
205
+ <div class="step-impact">
206
+ <h4>📈 Score Impact</h4>
207
+ <div class="impact-tags">${impactHtml}</div>
208
+ </div>
209
+ </div>`;
210
+ }
211
+ renderFooter() {
212
+ return `
213
+ <div class="footer">
214
+ <p>Generated by <a href="https://github.com/camilooscargbaptista/architect">🏗️ Architect v2.0</a> — Refactoring Engine</p>
215
+ <p>By <strong>Camilo Girardelli</strong> · <a href="https://www.girardellitecnologia.com">Girardelli Tecnologia</a></p>
216
+ </div>`;
217
+ }
218
+ getStyles() {
219
+ return `<style>
220
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap');
221
+ * { margin: 0; padding: 0; box-sizing: border-box; }
222
+ body {
223
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
224
+ background: #0f172a; color: #e2e8f0; line-height: 1.6; min-height: 100vh;
225
+ }
226
+ .container { max-width: 1100px; margin: 0 auto; padding: 2rem; }
227
+
228
+ .header {
229
+ text-align: center; padding: 3rem 2rem;
230
+ background: linear-gradient(135deg, #1e293b 0%, #0f172a 50%, #1e1b4b 100%);
231
+ border-bottom: 1px solid #334155; margin-bottom: 2rem;
232
+ }
233
+ .header h1 {
234
+ font-size: 2.5rem; font-weight: 900;
235
+ background: linear-gradient(135deg, #818cf8, #c084fc, #f472b6);
236
+ -webkit-background-clip: text; -webkit-text-fill-color: transparent;
237
+ margin-bottom: 0.5rem;
238
+ }
239
+ .header .subtitle { color: #94a3b8; font-size: 1.1rem; font-weight: 300; }
240
+ .header .meta {
241
+ margin-top: 1rem; display: flex; justify-content: center; gap: 1rem; flex-wrap: wrap;
242
+ }
243
+ .header .meta span {
244
+ background: #1e293b; padding: 0.4rem 1rem; border-radius: 99px;
245
+ font-size: 0.85rem; color: #94a3b8; border: 1px solid #334155;
246
+ }
247
+ .header .meta span strong { color: #e2e8f0; }
248
+
249
+ .section-title {
250
+ font-size: 1.4rem; font-weight: 700; margin: 2.5rem 0 1rem;
251
+ display: flex; align-items: center; gap: 0.5rem;
252
+ }
253
+
254
+ .card {
255
+ background: #1e293b; border-radius: 16px; border: 1px solid #334155;
256
+ padding: 1.5rem; margin-bottom: 1rem;
257
+ }
258
+ .success-card { border-color: #22c55e40; color: #22c55e; text-align: center; padding: 2rem; }
259
+
260
+ /* Score Comparison */
261
+ .score-comparison { padding: 2rem; }
262
+ .score-pair {
263
+ display: flex; align-items: center; justify-content: center; gap: 1.5rem;
264
+ margin-bottom: 2rem; flex-wrap: wrap;
265
+ }
266
+ .score-box { text-align: center; }
267
+ .score-num { font-size: 3.5rem; font-weight: 900; line-height: 1; }
268
+ .score-label { font-size: 0.85rem; color: #94a3b8; text-transform: uppercase; letter-spacing: 1px; }
269
+ .score-diff { font-size: 1.5rem; font-weight: 700; }
270
+
271
+ .bars-legend { display: flex; gap: 1rem; margin-bottom: 0.5rem; }
272
+ .legend-tag { font-size: 0.75rem; padding: 0.2rem 0.6rem; border-radius: 6px; }
273
+ .legend-tag.before { background: rgba(255,255,255,0.05); color: #94a3b8; }
274
+ .legend-tag.after { background: rgba(129,140,248,0.2); color: #818cf8; }
275
+
276
+ .comparison-row {
277
+ display: flex; align-items: center; gap: 1rem; margin-bottom: 0.5rem;
278
+ }
279
+ .metric-name { width: 100px; font-size: 0.8rem; text-transform: uppercase; color: #94a3b8; font-weight: 600; }
280
+ .metric-bars { flex: 1; position: relative; height: 30px; }
281
+ .bar-before, .bar-after {
282
+ position: absolute; top: 0; left: 0; height: 15px; border-radius: 4px;
283
+ display: flex; align-items: center; padding-left: 6px;
284
+ font-size: 0.7rem; font-weight: 600; transition: width 1s ease;
285
+ }
286
+ .bar-before { top: 0; }
287
+ .bar-after { top: 16px; }
288
+ .metric-diff { width: 50px; text-align: right; font-weight: 700; font-size: 0.85rem; }
289
+
290
+ /* Stats */
291
+ .stats-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; margin-bottom: 1rem; }
292
+ .stat-card {
293
+ background: linear-gradient(135deg, #1e293b, #0f172a);
294
+ border: 1px solid #334155; border-radius: 16px; padding: 1.5rem; text-align: center;
295
+ }
296
+ .stat-card .value {
297
+ font-size: 2rem; font-weight: 800;
298
+ background: linear-gradient(135deg, #818cf8, #c084fc);
299
+ -webkit-background-clip: text; -webkit-text-fill-color: transparent;
300
+ }
301
+ .stat-card .label { font-size: 0.85rem; color: #94a3b8; margin-top: 0.3rem; }
302
+
303
+ .priority-bar {
304
+ display: flex; border-radius: 12px; overflow: hidden; height: 32px; margin-bottom: 2rem;
305
+ }
306
+ .prio-seg {
307
+ display: flex; align-items: center; justify-content: center;
308
+ font-size: 0.75rem; font-weight: 600;
309
+ }
310
+ .prio-critical { background: #ef444430; color: #ef4444; }
311
+ .prio-high { background: #f59e0b30; color: #f59e0b; }
312
+ .prio-medium { background: #3b82f630; color: #60a5fa; }
313
+ .prio-low { background: #22c55e30; color: #22c55e; }
314
+
315
+ /* Steps */
316
+ .roadmap { display: flex; flex-direction: column; gap: 1rem; }
317
+ .step-card {
318
+ background: #1e293b; border-radius: 16px; border: 1px solid #334155;
319
+ padding: 1.5rem; transition: border-color 0.2s;
320
+ }
321
+ .step-card:hover { border-color: #818cf8; }
322
+ .step-header { display: flex; gap: 1rem; margin-bottom: 1rem; }
323
+ .step-number {
324
+ width: 40px; height: 40px; border-radius: 50%;
325
+ background: linear-gradient(135deg, #818cf8, #c084fc);
326
+ display: flex; align-items: center; justify-content: center;
327
+ font-weight: 800; font-size: 1rem; color: white; flex-shrink: 0;
328
+ }
329
+ .step-info { flex: 1; }
330
+ .step-title-row { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
331
+ .step-title-row h3 { font-size: 1.1rem; font-weight: 700; }
332
+ .step-desc { color: #94a3b8; font-size: 0.9rem; margin-top: 0.3rem; }
333
+
334
+ .severity-badge {
335
+ display: inline-block; padding: 0.15rem 0.5rem; border-radius: 99px;
336
+ font-size: 0.65rem; font-weight: 600; letter-spacing: 0.5px;
337
+ }
338
+ .severity-CRITICAL { background: #dc262620; color: #ef4444; border: 1px solid #ef444440; }
339
+ .severity-HIGH { background: #f59e0b20; color: #f59e0b; border: 1px solid #f59e0b40; }
340
+ .severity-MEDIUM { background: #3b82f620; color: #60a5fa; border: 1px solid #60a5fa40; }
341
+ .severity-LOW { background: #22c55e20; color: #22c55e; border: 1px solid #22c55e40; }
342
+
343
+ .tier-badge {
344
+ background: #818cf815; color: #818cf8; border: 1px solid #818cf830;
345
+ padding: 0.15rem 0.5rem; border-radius: 99px; font-size: 0.65rem; font-weight: 600;
346
+ }
347
+
348
+ .step-details { margin-top: 0.5rem; }
349
+ .step-details summary { cursor: pointer; color: #818cf8; font-size: 0.85rem; font-weight: 500; }
350
+ .rationale { color: #64748b; font-size: 0.85rem; margin-top: 0.3rem; font-style: italic; }
351
+
352
+ .step-operations { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid #334155; }
353
+ .step-operations h4 { font-size: 0.85rem; color: #94a3b8; margin-bottom: 0.5rem; }
354
+ .operation { display: flex; align-items: flex-start; gap: 0.5rem; margin-bottom: 0.5rem; flex-wrap: wrap; }
355
+ .op-icon { font-size: 0.9rem; }
356
+ .op-badge { padding: 0.1rem 0.4rem; border-radius: 6px; font-size: 0.65rem; font-weight: 700; }
357
+ .op-path { background: #0f172a; padding: 1px 6px; border-radius: 4px; font-size: 0.8rem; color: #c084fc; }
358
+ .op-arrow { color: #818cf8; font-weight: 700; }
359
+ .op-desc { width: 100%; color: #64748b; font-size: 0.8rem; padding-left: 1.8rem; }
360
+
361
+ .step-impact { margin-top: 0.5rem; }
362
+ .step-impact h4 { font-size: 0.85rem; color: #94a3b8; margin-bottom: 0.3rem; }
363
+ .impact-tags { display: flex; gap: 0.5rem; flex-wrap: wrap; }
364
+ .impact-tag {
365
+ background: #22c55e10; color: #22c55e; border: 1px solid #22c55e30;
366
+ padding: 0.2rem 0.6rem; border-radius: 8px; font-size: 0.75rem; font-weight: 500;
367
+ }
368
+ .impact-diff { font-weight: 700; }
369
+
370
+ .footer {
371
+ text-align: center; padding: 2rem; color: #475569; font-size: 0.85rem;
372
+ border-top: 1px solid #1e293b; margin-top: 3rem;
373
+ }
374
+ .footer a { color: #818cf8; text-decoration: none; }
375
+
376
+ @media (max-width: 768px) {
377
+ .stats-grid { grid-template-columns: repeat(2, 1fr); }
378
+ .score-pair { flex-direction: column; }
379
+ .container { padding: 1rem; }
380
+ }
381
+
382
+ @media print {
383
+ body { background: white; color: #1e293b; }
384
+ .card, .step-card, .stat-card { background: white; border-color: #e2e8f0; }
385
+ }
386
+ </style>`;
387
+ }
388
+ }
389
+ //# sourceMappingURL=refactor-reporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refactor-reporter.js","sourceRoot":"","sources":["../src/refactor-reporter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { AnalysisReport, RefactorRule, RefactorStep } from '../types.js';
2
+ /**
3
+ * Barrel Optimizer Rule (Tier 1)
4
+ * Analyzes barrel files (__init__.py, index.ts) and suggests optimization.
5
+ * Barrel files that re-export everything create unnecessary coupling.
6
+ */
7
+ export declare class BarrelOptimizerRule implements RefactorRule {
8
+ name: string;
9
+ tier: 1;
10
+ private static readonly BARREL_FILES;
11
+ analyze(report: AnalysisReport, projectPath: string): RefactorStep[];
12
+ }
13
+ //# sourceMappingURL=barrel-optimizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"barrel-optimizer.d.ts","sourceRoot":"","sources":["../../src/rules/barrel-optimizer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAiB,MAAM,aAAa,CAAC;AAExF;;;;GAIG;AACH,qBAAa,mBAAoB,YAAW,YAAY;IACtD,IAAI,SAAsB;IAC1B,IAAI,EAAG,CAAC,CAAU;IAElB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAEjC;IAEH,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,GAAG,YAAY,EAAE;CAgFrE"}
@@ -0,0 +1,77 @@
1
+ import { basename, dirname } from 'path';
2
+ /**
3
+ * Barrel Optimizer Rule (Tier 1)
4
+ * Analyzes barrel files (__init__.py, index.ts) and suggests optimization.
5
+ * Barrel files that re-export everything create unnecessary coupling.
6
+ */
7
+ export class BarrelOptimizerRule {
8
+ constructor() {
9
+ this.name = 'barrel-optimizer';
10
+ this.tier = 1;
11
+ }
12
+ analyze(report, projectPath) {
13
+ const steps = [];
14
+ // Find barrel files in the dependency graph
15
+ const barrelNodes = report.dependencyGraph.nodes.filter((n) => BarrelOptimizerRule.BARREL_FILES.has(basename(n)));
16
+ for (const barrel of barrelNodes) {
17
+ // Count how many things this barrel re-exports (outgoing edges)
18
+ const outgoing = report.dependencyGraph.edges.filter((e) => e.from === barrel);
19
+ const incoming = report.dependencyGraph.edges.filter((e) => e.to === barrel);
20
+ if (outgoing.length < 3)
21
+ continue;
22
+ // Check for "pass-through" pattern: files import from barrel
23
+ // but barrel just re-exports from siblings
24
+ const siblingDir = dirname(barrel);
25
+ const siblingExports = outgoing.filter((e) => dirname(e.to) === siblingDir);
26
+ const operations = [];
27
+ // Suggest direct imports instead of barrel
28
+ for (const consumer of incoming) {
29
+ const consumedModules = outgoing
30
+ .filter((e) => {
31
+ // Check if consumer actually needs this module
32
+ return report.dependencyGraph.edges.some((edge) => edge.from === consumer.from && edge.to === e.to);
33
+ })
34
+ .map((e) => e.to);
35
+ if (consumedModules.length > 0) {
36
+ operations.push({
37
+ type: 'MODIFY',
38
+ path: consumer.from,
39
+ description: `Replace barrel import from \`${basename(barrel)}\` with direct imports: ${consumedModules.map((m) => basename(m)).join(', ')}`,
40
+ });
41
+ }
42
+ }
43
+ // Suggest simplifying the barrel
44
+ if (siblingExports.length > 5) {
45
+ operations.push({
46
+ type: 'MODIFY',
47
+ path: barrel,
48
+ description: `Simplify ${basename(barrel)}: only re-export public API (${siblingExports.length} re-exports detected, consider reducing)`,
49
+ });
50
+ }
51
+ if (operations.length > 0) {
52
+ steps.push({
53
+ id: 0,
54
+ tier: 1,
55
+ rule: this.name,
56
+ priority: outgoing.length >= 8 ? 'HIGH' : 'MEDIUM',
57
+ title: `Optimize barrel: ${barrel}`,
58
+ description: `\`${barrel}\` re-exports ${outgoing.length} modules. ` +
59
+ `This creates a "Shotgun Surgery" risk — any change propagates widely.`,
60
+ rationale: `Barrel files that re-export everything make it hard to tree-shake unused code ` +
61
+ `and create implicit dependencies. Direct imports make dependency relationships explicit ` +
62
+ `and reduce the blast radius of changes.`,
63
+ operations,
64
+ scoreImpact: [
65
+ { metric: 'coupling', before: report.score.breakdown.coupling, after: Math.min(95, report.score.breakdown.coupling + 10) },
66
+ { metric: 'layering', before: report.score.breakdown.layering, after: Math.min(95, report.score.breakdown.layering + 5) },
67
+ ],
68
+ });
69
+ }
70
+ }
71
+ return steps;
72
+ }
73
+ }
74
+ BarrelOptimizerRule.BARREL_FILES = new Set([
75
+ '__init__.py', 'index.ts', 'index.js', 'index.tsx', 'index.jsx',
76
+ ]);
77
+ //# sourceMappingURL=barrel-optimizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"barrel-optimizer.js","sourceRoot":"","sources":["../../src/rules/barrel-optimizer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { AnalysisReport, RefactorRule, RefactorStep } from '../types.js';
2
+ /**
3
+ * Dead Code Detector Rule (Tier 1)
4
+ * Finds files with no incoming edges (nobody imports them)
5
+ * and exports that are never used.
6
+ *
7
+ * Handles both path-style (deepguard/report.py) and
8
+ * dot-notation (deepguard.report) references.
9
+ */
10
+ export declare class DeadCodeDetectorRule implements RefactorRule {
11
+ name: string;
12
+ tier: 1;
13
+ private static readonly ENTRY_POINTS;
14
+ analyze(report: AnalysisReport, projectPath: string): RefactorStep[];
15
+ /**
16
+ * Generate dot-notation variants for a file path.
17
+ * "deepguard/report.py" → ["deepguard.report", ".report"]
18
+ */
19
+ private getDotVariants;
20
+ }
21
+ //# sourceMappingURL=dead-code-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dead-code-detector.d.ts","sourceRoot":"","sources":["../../src/rules/dead-code-detector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAiB,MAAM,aAAa,CAAC;AAExF;;;;;;;GAOG;AACH,qBAAa,oBAAqB,YAAW,YAAY;IACvD,IAAI,SAAwB;IAC5B,IAAI,EAAG,CAAC,CAAU;IAElB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAIjC;IAEH,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,GAAG,YAAY,EAAE;IA0FpE;;;OAGG;IACH,OAAO,CAAC,cAAc;CAgBvB"}
@@ -0,0 +1,117 @@
1
+ import { basename } from 'path';
2
+ /**
3
+ * Dead Code Detector Rule (Tier 1)
4
+ * Finds files with no incoming edges (nobody imports them)
5
+ * and exports that are never used.
6
+ *
7
+ * Handles both path-style (deepguard/report.py) and
8
+ * dot-notation (deepguard.report) references.
9
+ */
10
+ export class DeadCodeDetectorRule {
11
+ constructor() {
12
+ this.name = 'dead-code-detector';
13
+ this.tier = 1;
14
+ }
15
+ analyze(report, projectPath) {
16
+ const steps = [];
17
+ const edges = report.dependencyGraph.edges;
18
+ // Build a set of ALL referenced targets (both path and dot-notation)
19
+ const allTargets = new Set();
20
+ const allSources = new Set();
21
+ for (const edge of edges) {
22
+ allTargets.add(edge.to);
23
+ allSources.add(edge.from);
24
+ }
25
+ // Only consider actual files (with path separators) as candidates
26
+ const fileNodes = report.dependencyGraph.nodes.filter((n) => n.includes('/') || n.includes('\\'));
27
+ // Build incoming edge count considering dot-notation matches
28
+ const incomingCount = {};
29
+ for (const file of fileNodes) {
30
+ incomingCount[file] = 0;
31
+ // Direct incoming edges
32
+ for (const edge of edges) {
33
+ if (edge.to === file) {
34
+ incomingCount[file]++;
35
+ }
36
+ }
37
+ // Check dot-notation references:
38
+ // deepguard/report.py might be referenced as deepguard.report or .report
39
+ const dotVariants = this.getDotVariants(file);
40
+ for (const variant of dotVariants) {
41
+ if (allTargets.has(variant)) {
42
+ incomingCount[file]++;
43
+ }
44
+ }
45
+ }
46
+ // Find orphan files
47
+ const orphans = [];
48
+ for (const [file, count] of Object.entries(incomingCount)) {
49
+ const fileName = basename(file);
50
+ // Skip entry points and config files
51
+ if (DeadCodeDetectorRule.ENTRY_POINTS.has(fileName))
52
+ continue;
53
+ if (fileName.startsWith('__'))
54
+ continue;
55
+ if (fileName.startsWith('.'))
56
+ continue;
57
+ if (fileName.endsWith('.test.ts') || fileName.endsWith('.spec.ts'))
58
+ continue;
59
+ if (fileName.endsWith('_test.py') || fileName.endsWith('.test.py'))
60
+ continue;
61
+ // Also skip if the file has outgoing edges (it's active code)
62
+ if (allSources.has(file))
63
+ continue;
64
+ if (count === 0) {
65
+ orphans.push(file);
66
+ }
67
+ }
68
+ if (orphans.length > 0) {
69
+ const operations = orphans.map((file) => ({
70
+ type: 'DELETE',
71
+ path: file,
72
+ description: `\`${basename(file)}\` has no incoming dependencies — verify if still needed`,
73
+ }));
74
+ steps.push({
75
+ id: 0,
76
+ tier: 1,
77
+ rule: this.name,
78
+ priority: orphans.length >= 3 ? 'MEDIUM' : 'LOW',
79
+ title: `Review ${orphans.length} potentially unused file(s)`,
80
+ description: `Found ${orphans.length} file(s) with no incoming dependencies: ` +
81
+ `${orphans.map((f) => `\`${basename(f)}\``).join(', ')}. ` +
82
+ `These may be dead code or missing from the module's public API.`,
83
+ rationale: `Files with zero incoming edges are either entry points (already excluded), ` +
84
+ `or potentially dead code. Removing dead code reduces maintenance burden ` +
85
+ `and improves modularity scores.`,
86
+ operations,
87
+ scoreImpact: [
88
+ { metric: 'modularity', before: report.score.breakdown.modularity, after: Math.min(95, report.score.breakdown.modularity + 5) },
89
+ ],
90
+ });
91
+ }
92
+ return steps;
93
+ }
94
+ /**
95
+ * Generate dot-notation variants for a file path.
96
+ * "deepguard/report.py" → ["deepguard.report", ".report"]
97
+ */
98
+ getDotVariants(filePath) {
99
+ const variants = [];
100
+ const withoutExt = filePath.replace(/\.[^.]+$/, '');
101
+ const dotPath = withoutExt.replace(/[/\\]/g, '.');
102
+ variants.push(dotPath);
103
+ // Relative dot-notation: .report
104
+ const parts = filePath.split('/');
105
+ if (parts.length >= 2) {
106
+ const lastPart = parts[parts.length - 1].replace(/\.[^.]+$/, '');
107
+ variants.push(`.${lastPart}`);
108
+ }
109
+ return variants;
110
+ }
111
+ }
112
+ DeadCodeDetectorRule.ENTRY_POINTS = new Set([
113
+ 'main.py', 'cli.py', 'app.py', 'manage.py', 'wsgi.py', 'asgi.py',
114
+ 'main.ts', 'main.js', 'app.ts', 'app.js', 'server.ts', 'server.js',
115
+ 'index.html', 'setup.py', 'setup.cfg', 'pyproject.toml',
116
+ ]);
117
+ //# sourceMappingURL=dead-code-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dead-code-detector.js","sourceRoot":"","sources":["../../src/rules/dead-code-detector.ts"],"names":[],"mappings":"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"}