@girardelli/architect 2.1.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.
- package/dist/agent-generator.d.ts +15 -4
- package/dist/agent-generator.d.ts.map +1 -1
- package/dist/agent-generator.js +136 -33
- package/dist/agent-generator.js.map +1 -1
- package/dist/cli.js +61 -18
- package/dist/cli.js.map +1 -1
- package/dist/html-reporter.d.ts.map +1 -1
- package/dist/html-reporter.js +356 -98
- package/dist/html-reporter.js.map +1 -1
- package/package.json +1 -1
- package/src/agent-generator.ts +175 -50
- package/src/cli.ts +72 -18
- package/src/html-reporter.ts +363 -105
package/dist/html-reporter.js
CHANGED
|
@@ -17,17 +17,62 @@ ${this.getStyles()}
|
|
|
17
17
|
</head>
|
|
18
18
|
<body>
|
|
19
19
|
${this.renderHeader(report)}
|
|
20
|
-
<div class="
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
<div class="report-layout">
|
|
21
|
+
<nav class="sidebar" id="reportSidebar">
|
|
22
|
+
<div class="sidebar-title">Navigation</div>
|
|
23
|
+
<a href="#score" class="sidebar-link active" data-section="score">📊 Score</a>
|
|
24
|
+
<a href="#layers" class="sidebar-link" data-section="layers">📐 Layers & Graph</a>
|
|
25
|
+
<a href="#anti-patterns" class="sidebar-link" data-section="anti-patterns">⚠️ Anti-Patterns (${report.antiPatterns.length})</a>
|
|
26
|
+
<a href="#suggestions" class="sidebar-link" data-section="suggestions">💡 Suggestions (${report.suggestions.length})</a>
|
|
27
|
+
${plan ? `<a href="#refactoring" class="sidebar-link" data-section="refactoring">🔧 Refactoring (${plan.steps.length})</a>` : ''}
|
|
28
|
+
${agentSuggestion ? `<a href="#agents" class="sidebar-link" data-section="agents">🤖 Agents</a>` : ''}
|
|
29
|
+
</nav>
|
|
30
|
+
<button class="sidebar-toggle" onclick="document.getElementById('reportSidebar').classList.toggle('sidebar-open')">☰</button>
|
|
31
|
+
|
|
32
|
+
<div class="container">
|
|
33
|
+
<div id="score">
|
|
34
|
+
${this.renderScoreHero(report)}
|
|
35
|
+
${this.renderRadarChart(report)}
|
|
36
|
+
${this.renderStats(report)}
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<details class="section-accordion" id="layers" open>
|
|
40
|
+
<summary class="section-accordion-header">📐 Layer Analysis & Dependencies</summary>
|
|
41
|
+
<div class="section-accordion-body">
|
|
42
|
+
${this.renderLayers(report)}
|
|
43
|
+
${this.renderDependencyGraph(report)}
|
|
44
|
+
</div>
|
|
45
|
+
</details>
|
|
46
|
+
|
|
47
|
+
<details class="section-accordion" id="anti-patterns" open>
|
|
48
|
+
<summary class="section-accordion-header">⚠️ Anti-Patterns (${report.antiPatterns.length})</summary>
|
|
49
|
+
<div class="section-accordion-body">
|
|
50
|
+
${this.renderAntiPatternBubbles(report, grouped)}
|
|
51
|
+
${this.renderAntiPatterns(report, grouped)}
|
|
52
|
+
</div>
|
|
53
|
+
</details>
|
|
54
|
+
|
|
55
|
+
<details class="section-accordion" id="suggestions">
|
|
56
|
+
<summary class="section-accordion-header">💡 Suggestions (${report.suggestions.length})</summary>
|
|
57
|
+
<div class="section-accordion-body">
|
|
58
|
+
${this.renderSuggestions(sugGrouped)}
|
|
59
|
+
</div>
|
|
60
|
+
</details>
|
|
61
|
+
|
|
62
|
+
${plan ? `<details class="section-accordion" id="refactoring" open>
|
|
63
|
+
<summary class="section-accordion-header">🔧 Refactoring Plan (${plan.steps.length} steps, ${plan.totalOperations} operations)</summary>
|
|
64
|
+
<div class="section-accordion-body">
|
|
65
|
+
${this.renderRefactoringPlan(plan)}
|
|
66
|
+
</div>
|
|
67
|
+
</details>` : ''}
|
|
68
|
+
|
|
69
|
+
${agentSuggestion ? `<details class="section-accordion" id="agents" open>
|
|
70
|
+
<summary class="section-accordion-header">🤖 Agent System</summary>
|
|
71
|
+
<div class="section-accordion-body">
|
|
72
|
+
${this.renderAgentSuggestions(agentSuggestion)}
|
|
73
|
+
</div>
|
|
74
|
+
</details>` : ''}
|
|
75
|
+
</div>
|
|
31
76
|
</div>
|
|
32
77
|
${this.renderFooter()}
|
|
33
78
|
${this.getScripts(report)}
|
|
@@ -208,44 +253,70 @@ ${this.getScripts(report)}
|
|
|
208
253
|
renderDependencyGraph(report) {
|
|
209
254
|
if (report.dependencyGraph.edges.length === 0)
|
|
210
255
|
return '';
|
|
211
|
-
// Build
|
|
256
|
+
// Build real file set — only files that appear as SOURCE in edges (these are real scanned files)
|
|
257
|
+
const realFiles = new Set(report.dependencyGraph.edges.map(e => e.from));
|
|
258
|
+
// Count connections only for real files
|
|
212
259
|
const connectionCount = {};
|
|
213
260
|
for (const edge of report.dependencyGraph.edges) {
|
|
214
|
-
|
|
215
|
-
|
|
261
|
+
if (realFiles.has(edge.from)) {
|
|
262
|
+
connectionCount[edge.from] = (connectionCount[edge.from] || 0) + 1;
|
|
263
|
+
}
|
|
264
|
+
if (realFiles.has(edge.to)) {
|
|
265
|
+
connectionCount[edge.to] = (connectionCount[edge.to] || 0) + 1;
|
|
266
|
+
}
|
|
216
267
|
}
|
|
268
|
+
// Build layer map from report layers
|
|
217
269
|
const layerMap = {};
|
|
218
270
|
for (const layer of report.layers) {
|
|
219
271
|
for (const file of layer.files) {
|
|
220
272
|
layerMap[file] = layer.name;
|
|
221
273
|
}
|
|
222
274
|
}
|
|
223
|
-
|
|
275
|
+
// Create nodes only from real files
|
|
276
|
+
const allNodes = [...realFiles].map(n => ({
|
|
224
277
|
id: n,
|
|
225
278
|
name: n.split('/').pop() || n,
|
|
226
279
|
connections: connectionCount[n] || 0,
|
|
227
280
|
layer: layerMap[n] || 'Other',
|
|
228
281
|
}));
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
282
|
+
// Build links only between real files
|
|
283
|
+
const allLinks = report.dependencyGraph.edges
|
|
284
|
+
.filter(e => realFiles.has(e.from) && realFiles.has(e.to))
|
|
285
|
+
.map(e => ({ source: e.from, target: e.to }));
|
|
286
|
+
// Limit to top N most-connected nodes for large projects
|
|
287
|
+
const maxNodes = 60;
|
|
288
|
+
const sortedNodes = [...allNodes].sort((a, b) => b.connections - a.connections);
|
|
289
|
+
const limitedNodes = sortedNodes.slice(0, maxNodes);
|
|
290
|
+
const limitedNodeIds = new Set(limitedNodes.map(n => n.id));
|
|
291
|
+
const limitedLinks = allLinks.filter(l => limitedNodeIds.has(l.source) && limitedNodeIds.has(l.target));
|
|
292
|
+
const isLimited = allNodes.length > maxNodes;
|
|
293
|
+
// Collect unique layers from limited nodes
|
|
294
|
+
const uniqueLayers = [...new Set(limitedNodes.map(n => n.layer))];
|
|
233
295
|
return `
|
|
234
296
|
<h2 class="section-title">🔗 Dependency Graph</h2>
|
|
235
297
|
<div class="card graph-card">
|
|
236
|
-
<div class="graph-
|
|
237
|
-
<
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
298
|
+
<div class="graph-controls">
|
|
299
|
+
<div class="graph-legend">
|
|
300
|
+
<span class="legend-item"><span class="legend-dot" style="background: #ec4899"></span> API</span>
|
|
301
|
+
<span class="legend-item"><span class="legend-dot" style="background: #3b82f6"></span> Service</span>
|
|
302
|
+
<span class="legend-item"><span class="legend-dot" style="background: #10b981"></span> Data</span>
|
|
303
|
+
<span class="legend-item"><span class="legend-dot" style="background: #f59e0b"></span> UI</span>
|
|
304
|
+
<span class="legend-item"><span class="legend-dot" style="background: #8b5cf6"></span> Infra</span>
|
|
305
|
+
<span class="legend-item"><span class="legend-dot" style="background: #64748b"></span> Other</span>
|
|
306
|
+
</div>
|
|
307
|
+
<div class="graph-filters">
|
|
308
|
+
<input type="text" id="graphSearch" class="graph-search" placeholder="🔍 Search node..." oninput="filterGraphNodes(this.value)">
|
|
309
|
+
<div class="graph-layer-filters">
|
|
310
|
+
${uniqueLayers.map(l => `<label class="graph-filter-check"><input type="checkbox" checked data-layer="${l}" onchange="toggleGraphLayer('${l}', this.checked)"><span class="legend-dot" style="background: ${{ 'API': '#ec4899', 'Service': '#3b82f6', 'Data': '#10b981', 'UI': '#f59e0b', 'Infrastructure': '#8b5cf6' }[l] || '#64748b'}"></span> ${l}</label>`).join('')}
|
|
311
|
+
</div>
|
|
312
|
+
</div>
|
|
313
|
+
${isLimited ? `<div class="graph-limit-notice">Showing top ${maxNodes} of ${allNodes.length} source files (most connected) · ${limitedLinks.length} links</div>` : ''}
|
|
243
314
|
</div>
|
|
244
|
-
<div id="dep-graph" style="width:100%; min-height:
|
|
245
|
-
<div class="graph-hint">🖱️ Drag nodes to
|
|
315
|
+
<div id="dep-graph" style="width:100%; min-height:500px;"></div>
|
|
316
|
+
<div class="graph-hint">🖱️ Drag nodes • Scroll to zoom • Double-click to reset • Node size = connections</div>
|
|
246
317
|
</div>
|
|
247
|
-
<script type="application/json" id="graph-nodes">${JSON.stringify(
|
|
248
|
-
<script type="application/json" id="graph-links">${JSON.stringify(
|
|
318
|
+
<script type="application/json" id="graph-nodes">${JSON.stringify(limitedNodes)}<\\/script>
|
|
319
|
+
<script type="application/json" id="graph-links">${JSON.stringify(limitedLinks)}<\\/script>`;
|
|
249
320
|
}
|
|
250
321
|
/**
|
|
251
322
|
* Bubble chart for anti-patterns — bigger = more severe
|
|
@@ -476,10 +547,12 @@ ${this.getScripts(report)}
|
|
|
476
547
|
</details>
|
|
477
548
|
</div>
|
|
478
549
|
</div>
|
|
479
|
-
<
|
|
480
|
-
<
|
|
481
|
-
|
|
482
|
-
|
|
550
|
+
<details class="rstep-ops-accordion">
|
|
551
|
+
<summary class="rstep-ops-toggle">📋 Operations (${step.operations.length})</summary>
|
|
552
|
+
<div class="rstep-ops">
|
|
553
|
+
${operationsHtml}
|
|
554
|
+
</div>
|
|
555
|
+
</details>
|
|
483
556
|
<div class="rstep-impact">
|
|
484
557
|
<h4>📈 Score Impact</h4>
|
|
485
558
|
<div class="rimpact-tags">${impactHtml}</div>
|
|
@@ -507,6 +580,23 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
507
580
|
}, { threshold: 0.5 });
|
|
508
581
|
|
|
509
582
|
counters.forEach(c => observer.observe(c));
|
|
583
|
+
|
|
584
|
+
// ── Sidebar Active Section Tracking ──
|
|
585
|
+
const sectionIds = ['score', 'layers', 'anti-patterns', 'suggestions', 'refactoring', 'agents'];
|
|
586
|
+
const sectionObserver = new IntersectionObserver((entries) => {
|
|
587
|
+
entries.forEach(entry => {
|
|
588
|
+
if (entry.isIntersecting) {
|
|
589
|
+
document.querySelectorAll('.sidebar-link').forEach(l => l.classList.remove('active'));
|
|
590
|
+
const link = document.querySelector('.sidebar-link[data-section="' + entry.target.id + '"]');
|
|
591
|
+
if (link) link.classList.add('active');
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
}, { threshold: 0.15, rootMargin: '-80px 0px -60% 0px' });
|
|
595
|
+
|
|
596
|
+
sectionIds.forEach(id => {
|
|
597
|
+
const el = document.getElementById(id);
|
|
598
|
+
if (el) sectionObserver.observe(el);
|
|
599
|
+
});
|
|
510
600
|
});
|
|
511
601
|
|
|
512
602
|
function animateCounter(el, target) {
|
|
@@ -609,7 +699,7 @@ function animateCounter(el, target) {
|
|
|
609
699
|
|
|
610
700
|
const container = document.getElementById('dep-graph');
|
|
611
701
|
const width = container.clientWidth || 800;
|
|
612
|
-
const height =
|
|
702
|
+
const height = 500;
|
|
613
703
|
container.style.height = height + 'px';
|
|
614
704
|
|
|
615
705
|
const layerColors = {
|
|
@@ -621,28 +711,43 @@ function animateCounter(el, target) {
|
|
|
621
711
|
.attr('width', width).attr('height', height)
|
|
622
712
|
.attr('viewBox', [0, 0, width, height]);
|
|
623
713
|
|
|
714
|
+
// Zoom container
|
|
715
|
+
const g = svg.append('g');
|
|
716
|
+
|
|
717
|
+
// Zoom behavior
|
|
718
|
+
const zoom = d3.zoom()
|
|
719
|
+
.scaleExtent([0.2, 5])
|
|
720
|
+
.on('zoom', (event) => { g.attr('transform', event.transform); });
|
|
721
|
+
svg.call(zoom);
|
|
722
|
+
|
|
723
|
+
// Double-click to reset zoom
|
|
724
|
+
svg.on('dblclick.zoom', () => {
|
|
725
|
+
svg.transition().duration(500).call(zoom.transform, d3.zoomIdentity);
|
|
726
|
+
});
|
|
727
|
+
|
|
624
728
|
// Arrow marker
|
|
625
|
-
|
|
729
|
+
g.append('defs').append('marker')
|
|
626
730
|
.attr('id', 'arrowhead').attr('viewBox', '-0 -5 10 10')
|
|
627
731
|
.attr('refX', 20).attr('refY', 0).attr('orient', 'auto')
|
|
628
732
|
.attr('markerWidth', 6).attr('markerHeight', 6)
|
|
629
733
|
.append('path').attr('d', 'M 0,-5 L 10,0 L 0,5')
|
|
630
734
|
.attr('fill', '#475569');
|
|
631
735
|
|
|
736
|
+
// Tuned simulation for better spread
|
|
632
737
|
const simulation = d3.forceSimulation(nodes)
|
|
633
|
-
.force('link', d3.forceLink(links).id(d => d.id).distance(
|
|
634
|
-
.force('charge', d3.forceManyBody().strength(-
|
|
738
|
+
.force('link', d3.forceLink(links).id(d => d.id).distance(80))
|
|
739
|
+
.force('charge', d3.forceManyBody().strength(-250))
|
|
635
740
|
.force('center', d3.forceCenter(width / 2, height / 2))
|
|
636
|
-
.force('x', d3.forceX(width / 2).strength(0.
|
|
637
|
-
.force('y', d3.forceY(height / 2).strength(0.
|
|
638
|
-
.force('collision', d3.forceCollide().radius(d => Math.max(d.connections *
|
|
741
|
+
.force('x', d3.forceX(width / 2).strength(0.05))
|
|
742
|
+
.force('y', d3.forceY(height / 2).strength(0.05))
|
|
743
|
+
.force('collision', d3.forceCollide().radius(d => Math.max(d.connections * 2 + 16, 20)));
|
|
639
744
|
|
|
640
|
-
const link =
|
|
745
|
+
const link = g.append('g')
|
|
641
746
|
.selectAll('line').data(links).join('line')
|
|
642
|
-
.attr('stroke', '#334155').attr('stroke-width', 1
|
|
643
|
-
.attr('stroke-opacity', 0.
|
|
747
|
+
.attr('stroke', '#334155').attr('stroke-width', 1)
|
|
748
|
+
.attr('stroke-opacity', 0.4).attr('marker-end', 'url(#arrowhead)');
|
|
644
749
|
|
|
645
|
-
const node =
|
|
750
|
+
const node = g.append('g')
|
|
646
751
|
.selectAll('g').data(nodes).join('g')
|
|
647
752
|
.call(d3.drag()
|
|
648
753
|
.on('start', (e, d) => { if (!e.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; })
|
|
@@ -650,37 +755,55 @@ function animateCounter(el, target) {
|
|
|
650
755
|
.on('end', (e, d) => { if (!e.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; })
|
|
651
756
|
);
|
|
652
757
|
|
|
653
|
-
// Node circles —
|
|
758
|
+
// Node circles — color by layer
|
|
654
759
|
node.append('circle')
|
|
655
|
-
.attr('r', d => Math.max(d.connections *
|
|
760
|
+
.attr('r', d => Math.max(d.connections * 2.5 + 5, 6))
|
|
656
761
|
.attr('fill', d => layerColors[d.layer] || '#64748b')
|
|
657
|
-
.attr('stroke', '#0f172a').attr('stroke-width',
|
|
658
|
-
.attr('opacity', 0.
|
|
762
|
+
.attr('stroke', '#0f172a').attr('stroke-width', 1.5)
|
|
763
|
+
.attr('opacity', 0.9);
|
|
659
764
|
|
|
660
|
-
// Node labels
|
|
661
|
-
node.append('text')
|
|
765
|
+
// Node labels — only show for nodes with enough connections
|
|
766
|
+
node.filter(d => d.connections >= 2).append('text')
|
|
662
767
|
.text(d => d.name.replace(/\\.[^.]+$/, ''))
|
|
663
|
-
.attr('x', 0).attr('y', d => -(Math.max(d.connections *
|
|
768
|
+
.attr('x', 0).attr('y', d => -(Math.max(d.connections * 2.5 + 5, 6) + 4))
|
|
664
769
|
.attr('text-anchor', 'middle')
|
|
665
|
-
.attr('fill', '#
|
|
770
|
+
.attr('fill', '#e2e8f0').attr('font-size', '9px').attr('font-weight', '500');
|
|
666
771
|
|
|
667
|
-
// Tooltip
|
|
772
|
+
// Tooltip
|
|
668
773
|
node.append('title')
|
|
669
774
|
.text(d => d.id + '\\nConnections: ' + d.connections + '\\nLayer: ' + d.layer);
|
|
670
775
|
|
|
671
776
|
simulation.on('tick', () => {
|
|
672
|
-
// Clamp nodes to stay within SVG bounds
|
|
673
|
-
nodes.forEach(d => {
|
|
674
|
-
const r = Math.max(d.connections * 3 + 6, 8) + 10;
|
|
675
|
-
d.x = Math.max(r, Math.min(width - r, d.x));
|
|
676
|
-
d.y = Math.max(r, Math.min(height - r, d.y));
|
|
677
|
-
});
|
|
678
|
-
|
|
679
777
|
link
|
|
680
778
|
.attr('x1', d => d.source.x).attr('y1', d => d.source.y)
|
|
681
779
|
.attr('x2', d => d.target.x).attr('y2', d => d.target.y);
|
|
682
780
|
node.attr('transform', d => 'translate(' + d.x + ',' + d.y + ')');
|
|
683
781
|
});
|
|
782
|
+
|
|
783
|
+
// Expose search and filter functions
|
|
784
|
+
window.filterGraphNodes = function(query) {
|
|
785
|
+
if (!query) {
|
|
786
|
+
node.attr('opacity', 1);
|
|
787
|
+
link.attr('opacity', 0.4);
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
query = query.toLowerCase();
|
|
791
|
+
node.attr('opacity', d => d.id.toLowerCase().includes(query) || d.name.toLowerCase().includes(query) ? 1 : 0.1);
|
|
792
|
+
link.attr('opacity', d => {
|
|
793
|
+
const srcMatch = d.source.id.toLowerCase().includes(query);
|
|
794
|
+
const tgtMatch = d.target.id.toLowerCase().includes(query);
|
|
795
|
+
return (srcMatch || tgtMatch) ? 0.6 : 0.05;
|
|
796
|
+
});
|
|
797
|
+
};
|
|
798
|
+
|
|
799
|
+
window.toggleGraphLayer = function(layer, visible) {
|
|
800
|
+
node.filter(d => d.layer === layer)
|
|
801
|
+
.transition().duration(300)
|
|
802
|
+
.attr('opacity', visible ? 1 : 0.05);
|
|
803
|
+
link.filter(d => d.source.layer === layer || d.target.layer === layer)
|
|
804
|
+
.transition().duration(300)
|
|
805
|
+
.attr('opacity', visible ? 0.4 : 0.02);
|
|
806
|
+
};
|
|
684
807
|
})();
|
|
685
808
|
|
|
686
809
|
// ── Bubble Chart ──
|
|
@@ -785,49 +908,60 @@ function animateCounter(el, target) {
|
|
|
785
908
|
return '#fbbf24';
|
|
786
909
|
return '#60a5fa';
|
|
787
910
|
};
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
911
|
+
// Status helpers
|
|
912
|
+
const statusBadge = (status) => {
|
|
913
|
+
const map = {
|
|
914
|
+
'KEEP': { icon: '✅', label: 'KEEP', color: '#22c55e' },
|
|
915
|
+
'MODIFY': { icon: '🔵', label: 'MODIFY', color: '#3b82f6' },
|
|
916
|
+
'CREATE': { icon: '🟡', label: 'NEW', color: '#f59e0b' },
|
|
917
|
+
'DELETE': { icon: '🔴', label: 'REMOVE', color: '#ef4444' },
|
|
918
|
+
};
|
|
919
|
+
const s = map[status] || map['CREATE'];
|
|
920
|
+
return `<span class="agent-status-badge" style="background:${s.color}20;color:${s.color};border:1px solid ${s.color}40">${s.icon} ${s.label}</span>`;
|
|
921
|
+
};
|
|
922
|
+
const statusBorder = (status) => {
|
|
923
|
+
const map = {
|
|
924
|
+
'KEEP': '#22c55e', 'MODIFY': '#3b82f6', 'CREATE': '#f59e0b', 'DELETE': '#ef4444',
|
|
925
|
+
};
|
|
926
|
+
return map[status] || '#334155';
|
|
927
|
+
};
|
|
928
|
+
const agentCards = s.suggestedAgents.map(a => `<label class="agent-toggle-card" data-category="agents" data-name="${a.name}">
|
|
929
|
+
<input type="checkbox" class="agent-check" ${a.status !== 'DELETE' ? 'checked' : ''} data-type="agents" data-item="${a.name}">
|
|
930
|
+
<div class="agent-toggle-inner" style="border-color:${statusBorder(a.status)}">
|
|
931
|
+
<div class="agent-toggle-icon">${roleIcon(a.name)}</div>
|
|
792
932
|
<div class="agent-toggle-info">
|
|
793
|
-
<span class="agent-toggle-name">${a}</span>
|
|
794
|
-
<span class="agent-toggle-role" style="color:${roleColor(a)}">${roleLabel(a)}</span>
|
|
933
|
+
<span class="agent-toggle-name">${a.name}</span>
|
|
934
|
+
<span class="agent-toggle-role" style="color:${roleColor(a.name)}">${roleLabel(a.name)}</span>
|
|
935
|
+
${a.description ? `<span class="agent-toggle-desc">${a.description}</span>` : ''}
|
|
795
936
|
</div>
|
|
937
|
+
${statusBadge(a.status)}
|
|
796
938
|
<div class="agent-toggle-check">\u2713</div>
|
|
797
939
|
</div>
|
|
798
940
|
</label>`).join('\n');
|
|
799
|
-
const
|
|
800
|
-
<input type="checkbox" class="agent-check" checked data-type="
|
|
801
|
-
<div class="agent-toggle-inner">
|
|
802
|
-
<span class="agent-toggle-icon"
|
|
803
|
-
<
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
<input type="checkbox" class="agent-check" checked data-type="guards" data-item="${g}">
|
|
809
|
-
<div class="agent-toggle-inner">
|
|
810
|
-
<span class="agent-toggle-icon">\u{1F6E1}\uFE0F</span>
|
|
811
|
-
<span class="agent-toggle-name">${g}.md</span>
|
|
812
|
-
<div class="agent-toggle-check">\u2713</div>
|
|
813
|
-
</div>
|
|
814
|
-
</label>`).join('\n');
|
|
815
|
-
const workflowCards = s.suggestedWorkflows.map(w => `<label class="agent-toggle-card mini" data-category="workflows">
|
|
816
|
-
<input type="checkbox" class="agent-check" checked data-type="workflows" data-item="${w}">
|
|
817
|
-
<div class="agent-toggle-inner">
|
|
818
|
-
<span class="agent-toggle-icon">\u26A1</span>
|
|
819
|
-
<span class="agent-toggle-name">${w}.md</span>
|
|
941
|
+
const miniCard = (item, icon, type) => `<label class="agent-toggle-card mini" data-category="${type}">
|
|
942
|
+
<input type="checkbox" class="agent-check" ${item.status !== 'DELETE' ? 'checked' : ''} data-type="${type}" data-item="${item.name}">
|
|
943
|
+
<div class="agent-toggle-inner" style="border-color:${statusBorder(item.status)}">
|
|
944
|
+
<span class="agent-toggle-icon">${icon}</span>
|
|
945
|
+
<div class="agent-toggle-info">
|
|
946
|
+
<span class="agent-toggle-name">${item.name}.md</span>
|
|
947
|
+
${item.description ? `<span class="agent-toggle-desc">${item.description}</span>` : ''}
|
|
948
|
+
</div>
|
|
949
|
+
${statusBadge(item.status)}
|
|
820
950
|
<div class="agent-toggle-check">\u2713</div>
|
|
821
951
|
</div>
|
|
822
|
-
</label
|
|
952
|
+
</label>`;
|
|
953
|
+
const ruleCards = s.suggestedRules.map(r => miniCard(r, '\u{1F4CF}', 'rules')).join('\n');
|
|
954
|
+
const guardCards = s.suggestedGuards.map(g => miniCard(g, '\u{1F6E1}\uFE0F', 'guards')).join('\n');
|
|
955
|
+
const workflowCards = s.suggestedWorkflows.map(w => miniCard(w, '\u26A1', 'workflows')).join('\n');
|
|
823
956
|
const skillCards = s.suggestedSkills.map(sk => `<label class="agent-toggle-card" data-category="skills">
|
|
824
957
|
<input type="checkbox" class="agent-check" checked data-type="skills" data-item="${sk.source}">
|
|
825
|
-
<div class="agent-toggle-inner">
|
|
958
|
+
<div class="agent-toggle-inner" style="border-color:${statusBorder(sk.status)}">
|
|
826
959
|
<span class="agent-toggle-icon">\u{1F9E0}</span>
|
|
827
960
|
<div class="agent-toggle-info">
|
|
828
961
|
<span class="agent-toggle-name">${sk.name}</span>
|
|
829
962
|
<span class="agent-toggle-role" style="color:#34d399">${sk.description}</span>
|
|
830
963
|
</div>
|
|
964
|
+
${statusBadge(sk.status)}
|
|
831
965
|
<div class="agent-toggle-check">\u2713</div>
|
|
832
966
|
</div>
|
|
833
967
|
</label>`).join('\n');
|
|
@@ -852,17 +986,31 @@ function animateCounter(el, target) {
|
|
|
852
986
|
`\u{1F527} ${s.stack.primary}`,
|
|
853
987
|
`\u{1F4E6} ${s.stack.frameworks.length > 0 ? s.stack.frameworks.join(', ') : 'No framework'}`,
|
|
854
988
|
s.hasExistingAgents ? '\u{1F4C1} Existing .agent/' : '\u{1F4C1} New .agent/',
|
|
855
|
-
|
|
989
|
+
...(s.stack.hasBackend ? ['\u{1F519} Backend'] : []),
|
|
990
|
+
...(s.stack.hasFrontend ? ['\u{1F5A5}\uFE0F Frontend'] : []),
|
|
991
|
+
...(s.stack.hasMobile ? ['\u{1F4F1} Mobile'] : []),
|
|
992
|
+
...(s.stack.hasDatabase ? ['\u{1F5C4}\uFE0F Database'] : []),
|
|
856
993
|
];
|
|
857
994
|
const totalItems = s.suggestedAgents.length + s.suggestedRules.length + s.suggestedGuards.length + s.suggestedWorkflows.length + s.suggestedSkills.length;
|
|
995
|
+
// Status summary counts
|
|
996
|
+
const allItems = [...s.suggestedAgents, ...s.suggestedRules, ...s.suggestedGuards, ...s.suggestedWorkflows];
|
|
997
|
+
const keepCount = allItems.filter(i => i.status === 'KEEP').length;
|
|
998
|
+
const modifyCount = allItems.filter(i => i.status === 'MODIFY').length;
|
|
999
|
+
const createCount = allItems.filter(i => i.status === 'CREATE').length;
|
|
858
1000
|
return `
|
|
859
|
-
<h2 class="section-title">\u{1F916} Agent System
|
|
1001
|
+
<h2 class="section-title">\u{1F916} Agent System</h2>
|
|
860
1002
|
|
|
861
1003
|
<div class="card agent-system-card">
|
|
862
1004
|
<div class="agent-stack-banner">
|
|
863
1005
|
${stackPills.map(p => `<div class="stack-pill">${p}</div>`).join('\n ')}
|
|
864
1006
|
</div>
|
|
865
1007
|
|
|
1008
|
+
<div class="agent-status-legend">
|
|
1009
|
+
<span class="status-legend-item"><span class="legend-dot" style="background:#22c55e"></span> KEEP (${keepCount})</span>
|
|
1010
|
+
<span class="status-legend-item"><span class="legend-dot" style="background:#3b82f6"></span> MODIFY (${modifyCount})</span>
|
|
1011
|
+
<span class="status-legend-item"><span class="legend-dot" style="background:#f59e0b"></span> NEW (${createCount})</span>
|
|
1012
|
+
</div>
|
|
1013
|
+
|
|
866
1014
|
<div class="agent-controls">
|
|
867
1015
|
<button class="agent-ctrl-btn" onclick="toggleAll(true)">\u2705 Select All</button>
|
|
868
1016
|
<button class="agent-ctrl-btn" onclick="toggleAll(false)">\u2B1C Select None</button>
|
|
@@ -910,18 +1058,22 @@ function animateCounter(el, target) {
|
|
|
910
1058
|
<style>
|
|
911
1059
|
.agent-system-card { padding: 1.5rem; }
|
|
912
1060
|
.agent-stack-banner { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 1.5rem; }
|
|
913
|
-
.stack-pill { background: #1e293b; border: 1px solid #334155; border-radius: 99px; padding: 0.4rem 1rem; font-size: 0.8rem; color: #94a3b8; white-space:
|
|
1061
|
+
.stack-pill { background: #1e293b; border: 1px solid #334155; border-radius: 99px; padding: 0.4rem 1rem; font-size: 0.8rem; color: #94a3b8; white-space: nowrap; }
|
|
1062
|
+
.agent-status-legend { display: flex; gap: 1.5rem; margin-bottom: 1rem; padding: 0.5rem 0; border-bottom: 1px solid #1e293b; }
|
|
1063
|
+
.status-legend-item { display: flex; align-items: center; gap: 0.4rem; font-size: 0.8rem; color: #94a3b8; }
|
|
1064
|
+
.agent-status-badge { display: inline-flex; align-items: center; gap: 0.25rem; padding: 0.15rem 0.5rem; border-radius: 99px; font-size: 0.65rem; font-weight: 700; flex-shrink: 0; letter-spacing: 0.03em; }
|
|
1065
|
+
.agent-toggle-desc { display: block; font-size: 0.65rem; color: #64748b; margin-top: 0.15rem; line-height: 1.3; }
|
|
914
1066
|
.agent-controls { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 1.5rem; }
|
|
915
1067
|
.agent-ctrl-btn { background: #1e293b; border: 1px solid #334155; color: #e2e8f0; padding: 0.4rem 1rem; border-radius: 8px; font-size: 0.8rem; cursor: pointer; transition: all 0.2s; }
|
|
916
1068
|
.agent-ctrl-btn:hover { background: #334155; }
|
|
917
1069
|
.agent-count-label { color: #94a3b8; font-size: 0.85rem; margin-left: auto; }
|
|
918
1070
|
#agentSelectedCount { color: #c084fc; font-weight: 700; }
|
|
919
1071
|
.agent-section-subtitle { color: #e2e8f0; font-size: 1.05rem; font-weight: 700; margin: 1.25rem 0 0.75rem; }
|
|
920
|
-
.agent-toggle-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(
|
|
1072
|
+
.agent-toggle-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 0.75rem; }
|
|
921
1073
|
.agent-toggle-card { cursor: pointer; transition: all 0.3s; }
|
|
922
1074
|
.agent-toggle-card input { display: none; }
|
|
923
1075
|
.agent-toggle-inner { display: flex; align-items: center; gap: 0.75rem; background: #1e293b; border: 2px solid #334155; border-radius: 12px; padding: 0.75rem 1rem; transition: all 0.3s; }
|
|
924
|
-
.agent-toggle-card input:checked + .agent-toggle-inner {
|
|
1076
|
+
.agent-toggle-card input:checked + .agent-toggle-inner { background: #1e1b4b; }
|
|
925
1077
|
.agent-toggle-icon { font-size: 1.3rem; flex-shrink: 0; }
|
|
926
1078
|
.agent-toggle-info { flex: 1; min-width: 0; }
|
|
927
1079
|
.agent-toggle-name { display: block; color: #e2e8f0; font-weight: 600; font-size: 0.85rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
@@ -992,7 +1144,50 @@ function animateCounter(el, target) {
|
|
|
992
1144
|
min-height: 100vh;
|
|
993
1145
|
}
|
|
994
1146
|
|
|
995
|
-
|
|
1147
|
+
html { scroll-behavior: smooth; }
|
|
1148
|
+
|
|
1149
|
+
/* ── Layout ── */
|
|
1150
|
+
.report-layout { display: flex; min-height: 100vh; }
|
|
1151
|
+
|
|
1152
|
+
.sidebar {
|
|
1153
|
+
position: sticky; top: 0; height: 100vh; width: 220px; min-width: 220px;
|
|
1154
|
+
background: linear-gradient(180deg, #0f172a 0%, #1e293b 100%);
|
|
1155
|
+
border-right: 1px solid #334155; padding: 1.5rem 0;
|
|
1156
|
+
display: flex; flex-direction: column; gap: 0.25rem;
|
|
1157
|
+
overflow-y: auto; z-index: 100;
|
|
1158
|
+
}
|
|
1159
|
+
.sidebar-title {
|
|
1160
|
+
font-size: 0.7rem; font-weight: 700; text-transform: uppercase;
|
|
1161
|
+
letter-spacing: 0.15em; color: #475569; padding: 0 1.25rem; margin-bottom: 0.75rem;
|
|
1162
|
+
}
|
|
1163
|
+
.sidebar-link {
|
|
1164
|
+
display: flex; align-items: center; gap: 0.5rem; padding: 0.6rem 1.25rem;
|
|
1165
|
+
color: #94a3b8; text-decoration: none; font-size: 0.8rem; font-weight: 500;
|
|
1166
|
+
border-left: 3px solid transparent; transition: all 0.2s;
|
|
1167
|
+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
1168
|
+
}
|
|
1169
|
+
.sidebar-link:hover { color: #e2e8f0; background: #1e293b; border-left-color: #475569; }
|
|
1170
|
+
.sidebar-link.active { color: #c084fc; background: #c084fc10; border-left-color: #c084fc; font-weight: 700; }
|
|
1171
|
+
|
|
1172
|
+
.sidebar-toggle {
|
|
1173
|
+
display: none; position: fixed; bottom: 1.5rem; right: 1.5rem; z-index: 200;
|
|
1174
|
+
width: 48px; height: 48px; border-radius: 50%; border: none;
|
|
1175
|
+
background: #c084fc; color: #0f172a; font-size: 1.2rem; cursor: pointer;
|
|
1176
|
+
box-shadow: 0 4px 16px rgba(192,132,252,0.4); transition: all 0.2s;
|
|
1177
|
+
}
|
|
1178
|
+
.sidebar-toggle:hover { transform: scale(1.1); }
|
|
1179
|
+
|
|
1180
|
+
@media (max-width: 1024px) {
|
|
1181
|
+
.sidebar {
|
|
1182
|
+
position: fixed; left: -240px; top: 0; width: 240px; min-width: 240px;
|
|
1183
|
+
transition: left 0.3s ease; box-shadow: none;
|
|
1184
|
+
}
|
|
1185
|
+
.sidebar.sidebar-open { left: 0; box-shadow: 4px 0 24px rgba(0,0,0,0.5); }
|
|
1186
|
+
.sidebar-toggle { display: flex; align-items: center; justify-content: center; }
|
|
1187
|
+
.report-layout { flex-direction: column; }
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
.container { max-width: 1200px; margin: 0 auto; padding: 2rem; flex: 1; min-width: 0; }
|
|
996
1191
|
|
|
997
1192
|
/* ── Header ── */
|
|
998
1193
|
.header {
|
|
@@ -1071,6 +1266,44 @@ function animateCounter(el, target) {
|
|
|
1071
1266
|
display: flex; align-items: center; gap: 0.5rem;
|
|
1072
1267
|
}
|
|
1073
1268
|
|
|
1269
|
+
/* ── Section Accordion ── */
|
|
1270
|
+
.section-accordion {
|
|
1271
|
+
margin: 1.5rem 0; border: 1px solid #334155; border-radius: 16px;
|
|
1272
|
+
background: transparent; overflow: hidden;
|
|
1273
|
+
}
|
|
1274
|
+
.section-accordion-header {
|
|
1275
|
+
cursor: pointer; list-style: none; display: flex; align-items: center; gap: 0.75rem;
|
|
1276
|
+
font-size: 1.3rem; font-weight: 700; color: #e2e8f0;
|
|
1277
|
+
padding: 1.25rem 1.5rem; background: linear-gradient(135deg, #1e293b, #0f172a);
|
|
1278
|
+
border-bottom: 1px solid transparent; transition: all 0.3s; user-select: none;
|
|
1279
|
+
}
|
|
1280
|
+
.section-accordion-header:hover { background: linear-gradient(135deg, #334155, #1e293b); }
|
|
1281
|
+
.section-accordion[open] > .section-accordion-header { border-bottom-color: #334155; }
|
|
1282
|
+
.section-accordion-header::after {
|
|
1283
|
+
content: '\\25B6'; margin-left: auto; font-size: 0.8rem; color: #818cf8;
|
|
1284
|
+
transition: transform 0.3s;
|
|
1285
|
+
}
|
|
1286
|
+
.section-accordion[open] > .section-accordion-header::after { transform: rotate(90deg); }
|
|
1287
|
+
.section-accordion-header::-webkit-details-marker { display: none; }
|
|
1288
|
+
.section-accordion-body { padding: 0.5rem 0; }
|
|
1289
|
+
|
|
1290
|
+
/* ── Operations Accordion (inside refactoring steps) ── */
|
|
1291
|
+
.rstep-ops-accordion {
|
|
1292
|
+
margin: 0.75rem 0; border: 1px solid #1e293b; border-radius: 10px; overflow: hidden;
|
|
1293
|
+
}
|
|
1294
|
+
.rstep-ops-toggle {
|
|
1295
|
+
cursor: pointer; list-style: none; display: flex; align-items: center; gap: 0.5rem;
|
|
1296
|
+
font-size: 0.9rem; font-weight: 600; color: #94a3b8;
|
|
1297
|
+
padding: 0.75rem 1rem; background: #0f172a; transition: all 0.2s;
|
|
1298
|
+
}
|
|
1299
|
+
.rstep-ops-toggle:hover { background: #1e293b; color: #e2e8f0; }
|
|
1300
|
+
.rstep-ops-toggle::after {
|
|
1301
|
+
content: '\\25B6'; margin-left: auto; font-size: 0.65rem; color: #818cf8;
|
|
1302
|
+
transition: transform 0.3s;
|
|
1303
|
+
}
|
|
1304
|
+
.rstep-ops-accordion[open] > .rstep-ops-toggle::after { transform: rotate(90deg); }
|
|
1305
|
+
.rstep-ops-toggle::-webkit-details-marker { display: none; }
|
|
1306
|
+
|
|
1074
1307
|
/* ── Cards ── */
|
|
1075
1308
|
.card {
|
|
1076
1309
|
background: #1e293b; border-radius: 16px; border: 1px solid #334155;
|
|
@@ -1080,17 +1313,42 @@ function animateCounter(el, target) {
|
|
|
1080
1313
|
|
|
1081
1314
|
/* ── Graph ── */
|
|
1082
1315
|
.graph-card { padding: 1rem; }
|
|
1316
|
+
.graph-controls { margin-bottom: 0.75rem; }
|
|
1083
1317
|
.graph-legend {
|
|
1084
1318
|
display: flex; gap: 1rem; flex-wrap: wrap; margin-bottom: 0.5rem;
|
|
1085
1319
|
justify-content: center;
|
|
1086
1320
|
}
|
|
1087
1321
|
.legend-item { display: flex; align-items: center; gap: 4px; font-size: 0.75rem; color: #94a3b8; }
|
|
1088
|
-
.legend-dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; }
|
|
1322
|
+
.legend-dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; flex-shrink: 0; }
|
|
1323
|
+
.graph-filters {
|
|
1324
|
+
display: flex; gap: 0.75rem; align-items: center; flex-wrap: wrap;
|
|
1325
|
+
justify-content: center; margin-top: 0.5rem;
|
|
1326
|
+
}
|
|
1327
|
+
.graph-search {
|
|
1328
|
+
background: #0f172a; border: 1px solid #334155; border-radius: 8px;
|
|
1329
|
+
padding: 0.4rem 0.75rem; color: #e2e8f0; font-size: 0.8rem;
|
|
1330
|
+
outline: none; width: 180px; transition: border-color 0.2s;
|
|
1331
|
+
}
|
|
1332
|
+
.graph-search:focus { border-color: #818cf8; }
|
|
1333
|
+
.graph-layer-filters {
|
|
1334
|
+
display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center;
|
|
1335
|
+
}
|
|
1336
|
+
.graph-filter-check {
|
|
1337
|
+
display: flex; align-items: center; gap: 4px;
|
|
1338
|
+
font-size: 0.75rem; color: #94a3b8; cursor: pointer;
|
|
1339
|
+
}
|
|
1340
|
+
.graph-filter-check input { width: 14px; height: 14px; accent-color: #818cf8; }
|
|
1341
|
+
.graph-limit-notice {
|
|
1342
|
+
text-align: center; font-size: 0.75rem; color: #f59e0b;
|
|
1343
|
+
background: #f59e0b15; padding: 0.3rem 0.75rem; border-radius: 6px;
|
|
1344
|
+
margin-top: 0.5rem;
|
|
1345
|
+
}
|
|
1089
1346
|
.graph-hint {
|
|
1090
1347
|
text-align: center; font-size: 0.75rem; color: #475569; margin-top: 0.5rem;
|
|
1091
1348
|
font-style: italic;
|
|
1092
1349
|
}
|
|
1093
|
-
#dep-graph svg { background: rgba(0,0,0,0.2); border-radius: 12px; }
|
|
1350
|
+
#dep-graph svg { background: rgba(0,0,0,0.2); border-radius: 12px; cursor: grab; }
|
|
1351
|
+
#dep-graph svg:active { cursor: grabbing; }
|
|
1094
1352
|
|
|
1095
1353
|
/* ── Layers Grid ── */
|
|
1096
1354
|
.layers-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 1rem; }
|