depwire-cli 1.0.7 → 1.1.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/index.js CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  stashChanges,
18
18
  updateFileInGraph,
19
19
  watchProject
20
- } from "./chunk-IOJJTKI4.js";
20
+ } from "./chunk-W3ZVSDFL.js";
21
21
  import {
22
22
  SimulationEngine,
23
23
  analyzeDeadCode,
@@ -31,7 +31,7 @@ import {
31
31
  parseProject,
32
32
  scanSecurity,
33
33
  searchSymbols
34
- } from "./chunk-SLGC72RW.js";
34
+ } from "./chunk-WLKW7X7G.js";
35
35
 
36
36
  // src/index.ts
37
37
  import { Command } from "commander";
@@ -533,6 +533,27 @@ function generateWhatIfHtml(currentVizData, simulatedVizData, simulationResult,
533
533
  target: e.target.split("::")[0]
534
534
  }));
535
535
  const removedFilePairsJson = JSON.stringify(removedFilePairs);
536
+ const affectedFilesJson = JSON.stringify(diff.affectedNodes);
537
+ const brokenImportFilesJson = JSON.stringify(diff.brokenImports.map((bi) => bi.file));
538
+ const brokenCount = diff.brokenImports.length;
539
+ const affectedCount = diff.affectedNodes.length;
540
+ const healthDeltaVal = healthDelta.delta;
541
+ let riskLevel;
542
+ let riskColor;
543
+ if (brokenCount > 10 || affectedCount > 20) {
544
+ riskLevel = "High";
545
+ riskColor = "#ef4444";
546
+ } else if (brokenCount > 3 || affectedCount > 5) {
547
+ riskLevel = "Medium";
548
+ riskColor = "#fbbf24";
549
+ } else {
550
+ riskLevel = "Low";
551
+ riskColor = "#4ade80";
552
+ }
553
+ const brokenColor = brokenCount > 0 ? "#ef4444" : "#4ade80";
554
+ const affectedColor = affectedCount > 0 ? "#ef4444" : "#4ade80";
555
+ const healthDeltaColor = healthDeltaVal < 0 ? "#ef4444" : healthDeltaVal > 0 ? "#4ade80" : "#6b7280";
556
+ const healthDeltaStr = healthDeltaVal > 0 ? `+${healthDeltaVal}` : healthDeltaVal === 0 ? "0" : `${healthDeltaVal}`;
536
557
  return `<!DOCTYPE html>
537
558
  <html lang="en">
538
559
  <head>
@@ -559,35 +580,42 @@ function generateWhatIfHtml(currentVizData, simulatedVizData, simulationResult,
559
580
  -webkit-text-fill-color: transparent;
560
581
  background-clip: text;
561
582
  }
562
- .health-banner {
583
+ .stats-bar {
563
584
  background: #0f1729;
564
585
  border: 1px solid #2a2a4a;
565
586
  border-radius: 8px;
566
- padding: 16px 24px;
587
+ padding: 12px 24px;
567
588
  margin: 16px 24px;
568
589
  display: flex;
569
590
  align-items: center;
570
- gap: 32px;
591
+ gap: 28px;
571
592
  flex-wrap: wrap;
572
593
  }
573
- .health-score {
574
- font-size: 22px;
575
- font-weight: 700;
576
- }
577
- .health-stat {
594
+ .stats-bar .stat {
595
+ display: flex;
596
+ align-items: center;
597
+ gap: 8px;
578
598
  font-size: 14px;
579
599
  color: #a0a0a0;
580
600
  }
581
- .health-stat strong {
582
- color: #e0e0e0;
601
+ .stats-bar .stat-val {
602
+ font-weight: 700;
583
603
  font-size: 18px;
584
604
  }
605
+ .stats-bar .risk-badge {
606
+ padding: 4px 14px;
607
+ border-radius: 4px;
608
+ font-weight: 700;
609
+ font-size: 13px;
610
+ text-transform: uppercase;
611
+ color: #000;
612
+ }
585
613
  .panels {
586
614
  display: flex;
587
615
  flex-direction: row;
588
616
  gap: 0;
589
617
  width: 100%;
590
- height: calc(100vh - 180px);
618
+ height: calc(100vh - 220px);
591
619
  min-height: 400px;
592
620
  }
593
621
  .panel {
@@ -621,12 +649,6 @@ function generateWhatIfHtml(currentVizData, simulatedVizData, simulationResult,
621
649
  width: 100%;
622
650
  height: 100%;
623
651
  }
624
- .broken-arc {
625
- stroke: #ef4444 !important;
626
- stroke-opacity: 1.0 !important;
627
- stroke-width: 2px !important;
628
- filter: drop-shadow(0 0 4px rgba(239, 68, 68, 0.6));
629
- }
630
652
  .broken-section {
631
653
  padding: 0 24px 24px;
632
654
  }
@@ -638,20 +660,17 @@ function generateWhatIfHtml(currentVizData, simulatedVizData, simulationResult,
638
660
  ${opBadge}
639
661
  </div>
640
662
 
641
- <div class="health-banner">
642
- <div class="health-score" style="color:${deltaColor}">
643
- Health Score: ${healthDelta.before} \u2192 ${healthDelta.after}
644
- <span style="font-size:16px;margin-left:8px;">(${deltaLabel})</span>
645
- </div>
646
- <div class="health-stat"><strong>${diff.affectedNodes.length}</strong> Affected Nodes</div>
647
- <div class="health-stat"><strong>${diff.brokenImports.length}</strong> Broken Imports</div>
648
- <div class="health-stat"><strong>${diff.removedEdges.length}</strong> Removed Edges</div>
663
+ <div class="stats-bar">
664
+ <div class="stat">Broken Imports: <span class="stat-val" style="color:${brokenColor}">${brokenCount}</span></div>
665
+ <div class="stat">Affected Files: <span class="stat-val" style="color:${affectedColor}">${affectedCount}</span></div>
666
+ <div class="stat">Health Score Delta: <span class="stat-val" style="color:${healthDeltaColor}">${healthDeltaStr}</span></div>
667
+ <div class="stat"><span class="risk-badge" style="background:${riskColor}">${riskLevel} Risk</span></div>
649
668
  </div>
650
669
 
651
670
  <div class="panels">
652
671
  <div class="panel">
653
672
  <div class="panel-label">
654
- <span>Current</span>
673
+ <span>Current State</span>
655
674
  <span>${currentVizData.stats.totalFiles} files</span>
656
675
  </div>
657
676
  <div class="panel-diagram" id="arc-diagram-current">
@@ -661,7 +680,7 @@ function generateWhatIfHtml(currentVizData, simulatedVizData, simulationResult,
661
680
  </div>
662
681
  <div class="panel">
663
682
  <div class="panel-label">
664
- <span>After ${operation !== "none" ? operation.toUpperCase() : "\u2014"}</span>
683
+ <span>After Simulation</span>
665
684
  <span>${simulatedVizData.stats.totalFiles} files</span>
666
685
  </div>
667
686
  <div class="panel-diagram" id="arc-diagram-simulated">
@@ -682,6 +701,8 @@ function generateWhatIfHtml(currentVizData, simulatedVizData, simulationResult,
682
701
  const currentData = ${currentDataJson};
683
702
  const simulatedData = ${simulatedDataJson};
684
703
  const removedFilePairs = ${removedFilePairsJson};
704
+ const affectedFiles = new Set(${affectedFilesJson});
705
+ const brokenImportFiles = new Set(${brokenImportFilesJson});
685
706
 
686
707
  // Inject broken arcs and ghost files into the simulated data
687
708
  // so they render on the right diagram and can be colored red
@@ -733,39 +754,88 @@ function generateWhatIfHtml(currentVizData, simulatedVizData, simulationResult,
733
754
  }
734
755
  }
735
756
 
757
+ // Mark affected arcs in simulated data
758
+ simulatedData.arcs.forEach(arc => {
759
+ if (affectedFiles.has(arc.sourceFile) || affectedFiles.has(arc.targetFile)) {
760
+ arc.affected = true;
761
+ }
762
+ });
763
+
764
+ // Mark affected file bars in simulated data
765
+ simulatedData.files.forEach(file => {
766
+ if (affectedFiles.has(file.path)) {
767
+ file.affected = true;
768
+ }
769
+ });
770
+
736
771
  const left = window.createArcDiagram('arc-diagram-current', 'svg-current', 'tooltip-current', currentData);
737
772
  const right = window.createArcDiagram('arc-diagram-simulated', 'svg-simulated', 'tooltip-simulated', simulatedData);
738
773
 
739
774
  left.render();
740
775
  right.render();
741
776
 
742
- // After render: color broken arcs red and ghost file bars red on the right diagram
743
- if (removedFilePairs.length > 0) {
744
- d3.select('#arc-diagram-simulated').selectAll('.arc')
745
- .filter(d => d.broken === true)
746
- .classed('broken-arc', true);
777
+ function applyGhostRedStyling() {
778
+ const simContainer = d3.select('#arc-diagram-simulated');
779
+ const hasAffected = affectedFiles.size > 0;
780
+
781
+ if (!hasAffected) return;
747
782
 
748
- d3.select('#arc-diagram-simulated').selectAll('.file-bar')
749
- .filter(d => d.ghost === true)
750
- .attr('fill', '#ef4444')
751
- .attr('opacity', 0.8);
783
+ // --- SVG filter for red glow on affected nodes ---
784
+ let defs = d3.select('#svg-simulated').select('defs');
785
+ if (defs.empty()) {
786
+ defs = d3.select('#svg-simulated').insert('defs', ':first-child');
787
+ }
788
+ if (defs.select('#red-glow').empty()) {
789
+ const filter = defs.append('filter').attr('id', 'red-glow').attr('x', '-50%').attr('y', '-50%').attr('width', '200%').attr('height', '200%');
790
+ filter.append('feDropShadow').attr('dx', 0).attr('dy', 0).attr('stdDeviation', 4).attr('flood-color', '#ef4444').attr('flood-opacity', 0.8);
791
+ }
792
+
793
+ // --- Edges ---
794
+ simContainer.selectAll('.arc').each(function(d) {
795
+ const el = d3.select(this);
796
+ if (d.broken) {
797
+ // Broken import edges: dashed red, thicker
798
+ el.attr('stroke', '#ef4444')
799
+ .attr('stroke-opacity', 1.0)
800
+ .attr('stroke-width', 3.0)
801
+ .attr('stroke-dasharray', '6,3')
802
+ .style('filter', null);
803
+ } else if (d.affected) {
804
+ // Affected edges: solid red
805
+ el.attr('stroke', '#ef4444')
806
+ .attr('stroke-opacity', 1.0)
807
+ .attr('stroke-width', 2.5)
808
+ .attr('stroke-dasharray', null)
809
+ .style('filter', null);
810
+ } else {
811
+ // Non-affected edges: ghost
812
+ el.attr('stroke-opacity', 0.08);
813
+ }
814
+ });
815
+
816
+ // --- Node bars ---
817
+ simContainer.selectAll('.file-bar').each(function(d) {
818
+ const el = d3.select(this);
819
+ if (d.affected || d.ghost) {
820
+ // Affected / ghost nodes: glowing red
821
+ el.attr('fill', '#ef4444')
822
+ .attr('opacity', 1.0)
823
+ .attr('stroke', '#ef4444')
824
+ .attr('stroke-width', 2)
825
+ .style('filter', 'url(#red-glow)');
826
+ } else {
827
+ // Non-affected nodes: ghost
828
+ el.attr('opacity', 0.15);
829
+ }
830
+ });
752
831
  }
753
832
 
833
+ applyGhostRedStyling();
834
+
754
835
  window.addEventListener('resize', () => {
755
836
  left.render();
756
837
  right.render();
757
-
758
- // Re-apply broken styling after resize re-render
759
- if (removedFilePairs.length > 0) {
760
- d3.select('#arc-diagram-simulated').selectAll('.arc')
761
- .filter(d => d.broken === true)
762
- .classed('broken-arc', true);
763
-
764
- d3.select('#arc-diagram-simulated').selectAll('.file-bar')
765
- .filter(d => d.ghost === true)
766
- .attr('fill', '#ef4444')
767
- .attr('opacity', 0.8);
768
- }
838
+ applyGhostRedStyling();
769
839
  });
770
840
  </script>
771
841
  </body>
@@ -4,11 +4,11 @@ import {
4
4
  startMcpServer,
5
5
  updateFileInGraph,
6
6
  watchProject
7
- } from "./chunk-IOJJTKI4.js";
7
+ } from "./chunk-W3ZVSDFL.js";
8
8
  import {
9
9
  buildGraph,
10
10
  parseProject
11
- } from "./chunk-SLGC72RW.js";
11
+ } from "./chunk-WLKW7X7G.js";
12
12
 
13
13
  // src/mcpb-entry.ts
14
14
  import { resolve } from "path";
package/dist/sdk.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  parseProject,
11
11
  scanSecurity,
12
12
  searchSymbols
13
- } from "./chunk-SLGC72RW.js";
13
+ } from "./chunk-WLKW7X7G.js";
14
14
 
15
15
  // src/sdk.ts
16
16
  import { readFileSync } from "fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "depwire-cli",
3
- "version": "1.0.7",
3
+ "version": "1.1.0",
4
4
  "description": "Dependency graph + 17 MCP tools for AI coding assistants. Impact analysis, health scoring, security scanner.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -41,7 +41,8 @@
41
41
  "refactor-simulation",
42
42
  "blast-radius",
43
43
  "deterministic-graph",
44
- "kotlin"
44
+ "kotlin",
45
+ "php"
45
46
  ],
46
47
  "author": "Atef Ataya (https://www.youtube.com/@atefataya)",
47
48
  "license": "BUSL-1.1",
@@ -76,6 +77,7 @@
76
77
  "tree-sitter-c-sharp": "^0.23.5",
77
78
  "tree-sitter-cpp": "^0.23.4",
78
79
  "tree-sitter-java": "^0.23.5",
80
+ "tree-sitter-php": "^0.24.2",
79
81
  "web-tree-sitter": "0.26.6",
80
82
  "ws": "8.19.0",
81
83
  "zod": "4.3.6"