depwire-cli 0.9.24 → 0.9.26

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/sdk.d.ts CHANGED
@@ -222,6 +222,54 @@ declare class SimulationEngine {
222
222
  private computeHealthScore;
223
223
  }
224
224
 
225
+ type Severity = 'critical' | 'high' | 'medium' | 'low' | 'info';
226
+ type VulnerabilityClass = 'dependency-cve' | 'shell-injection' | 'code-injection' | 'secrets' | 'path-traversal' | 'auth' | 'input-validation' | 'information-disclosure' | 'architecture' | 'cryptography' | 'supply-chain' | 'frontend-xss';
227
+ interface SecurityFinding {
228
+ id: string;
229
+ severity: Severity;
230
+ vulnerabilityClass: VulnerabilityClass;
231
+ file: string;
232
+ line?: number;
233
+ symbol?: string;
234
+ title: string;
235
+ description: string;
236
+ attackScenario: string;
237
+ suggestedFix: string;
238
+ graphReachability?: {
239
+ entryPoints: string[];
240
+ reachableFrom: number;
241
+ elevatedBy: string;
242
+ };
243
+ }
244
+ interface SecurityScanResult {
245
+ scannedAt: string;
246
+ projectRoot: string;
247
+ filesScanned: number;
248
+ findings: SecurityFinding[];
249
+ summary: {
250
+ critical: number;
251
+ high: number;
252
+ medium: number;
253
+ low: number;
254
+ info: number;
255
+ total: number;
256
+ };
257
+ dependencyAudit: {
258
+ ran: boolean;
259
+ packageManager: string | null;
260
+ rawOutput: string;
261
+ };
262
+ }
263
+ interface SecurityScanOptions {
264
+ target?: string;
265
+ classes?: VulnerabilityClass[];
266
+ format?: 'table' | 'json' | 'sarif';
267
+ failOn?: Severity;
268
+ graphAware?: boolean;
269
+ }
270
+
271
+ declare function scanSecurity(projectRoot: string, graph: DirectedGraph, options?: SecurityScanOptions): Promise<SecurityScanResult>;
272
+
225
273
  /**
226
274
  * depwire-cli SDK — Public API Surface
227
275
  *
@@ -234,4 +282,4 @@ declare class SimulationEngine {
234
282
  /** Current SDK version — matches depwire-cli npm version */
235
283
  declare const DepwireSDKVersion: string;
236
284
 
237
- export { type BrokenImport, DepwireSDKVersion, type GraphDiff, type HealthDelta, type SimulationAction, SimulationEngine, type SimulationResult, analyzeDeadCode, buildGraph, calculateHealthScore, generateDocs, getArchitectureSummary, getImpact, parseProject, searchSymbols };
285
+ export { type BrokenImport, DepwireSDKVersion, type GraphDiff, type HealthDelta, type SecurityFinding, type SecurityScanOptions, type SecurityScanResult, type Severity, type SimulationAction, SimulationEngine, type SimulationResult, type VulnerabilityClass, analyzeDeadCode, buildGraph, calculateHealthScore, generateDocs, getArchitectureSummary, getImpact, parseProject, scanSecurity, searchSymbols };
package/dist/sdk.js CHANGED
@@ -7,8 +7,9 @@ import {
7
7
  getArchitectureSummary,
8
8
  getImpact,
9
9
  parseProject,
10
+ scanSecurity,
10
11
  searchSymbols
11
- } from "./chunk-QHVWDUSX.js";
12
+ } from "./chunk-YYY5TNG7.js";
12
13
 
13
14
  // src/sdk.ts
14
15
  import { readFileSync } from "fs";
@@ -28,5 +29,6 @@ export {
28
29
  getArchitectureSummary,
29
30
  getImpact,
30
31
  parseProject,
32
+ scanSecurity,
31
33
  searchSymbols
32
34
  };
@@ -1,4 +1,176 @@
1
1
  // Arc Diagram Renderer using D3.js
2
+
3
+ // ── Factory function for creating isolated arc diagram instances ──
4
+ window.createArcDiagram = function(containerId, svgId, tooltipId, data) {
5
+ let _data = data;
6
+ let _svg = null;
7
+ let _g = null;
8
+ let _filePositions = new Map();
9
+ let _selectedFile = null;
10
+ let _selectedArc = null;
11
+
12
+ function _showTooltip(event, content) {
13
+ const el = document.getElementById(tooltipId);
14
+ if (!el) return;
15
+ el.innerHTML = content;
16
+ el.style.left = (event.pageX + 10) + 'px';
17
+ el.style.top = (event.pageY + 10) + 'px';
18
+ el.classList.add('show');
19
+ }
20
+
21
+ function _hideTooltip() {
22
+ const el = document.getElementById(tooltipId);
23
+ if (!el) return;
24
+ el.classList.remove('show');
25
+ }
26
+
27
+ function _clearSelection() {
28
+ _selectedFile = null;
29
+ _selectedArc = null;
30
+ const ctr = d3.select('#' + containerId);
31
+ ctr.selectAll('.arc').classed('highlighted', false).classed('dimmed', false);
32
+ ctr.selectAll('.file-bar').classed('highlighted', false).classed('dimmed', false);
33
+ }
34
+
35
+ function render() {
36
+ const container = document.getElementById(containerId);
37
+ if (!container || !_data) return;
38
+
39
+ const width = container.clientWidth;
40
+ const height = container.clientHeight;
41
+ _filePositions.clear();
42
+ _selectedFile = null;
43
+ _selectedArc = null;
44
+
45
+ const svgEl = d3.select('#' + svgId);
46
+ svgEl.selectAll('*').remove();
47
+ _svg = svgEl.attr('width', width).attr('height', height);
48
+ _g = _svg.append('g');
49
+
50
+ const zoom = d3.zoom().scaleExtent([0.5, 4]).on('zoom', (event) => {
51
+ _g.attr('transform', event.transform);
52
+ });
53
+ _svg.call(zoom);
54
+
55
+ const margin = { top: 60, right: 40, bottom: 120, left: 40 };
56
+ const plotWidth = width - margin.left - margin.right;
57
+ const plotHeight = height - margin.top - margin.bottom;
58
+ const baseline = margin.top + plotHeight;
59
+
60
+ const totalSymbols = d3.sum(_data.files, d => d.symbolCount);
61
+ const minBarWidth = 4;
62
+ const gap = 2;
63
+ let x = margin.left;
64
+
65
+ _data.files.forEach(file => {
66
+ const barWidth = Math.max(minBarWidth, (file.symbolCount / totalSymbols) * plotWidth * 0.8);
67
+ _filePositions.set(file.path, { x: x + barWidth / 2, width: barWidth, file: file });
68
+ x += barWidth + gap;
69
+ });
70
+
71
+ const directories = [...new Set(_data.files.map(f => f.directory))];
72
+ const colorScale = d3.scaleOrdinal().domain(directories)
73
+ .range(['#4a9eff', '#7c3aed', '#ec4899', '#f59e0b', '#10b981', '#06b6d4']);
74
+
75
+ const maxDistance = d3.max(_data.arcs, arc => {
76
+ const s = _filePositions.get(arc.sourceFile);
77
+ const t = _filePositions.get(arc.targetFile);
78
+ return (s && t) ? Math.abs(t.x - s.x) : 0;
79
+ }) || 1;
80
+
81
+ const ctr = d3.select('#' + containerId);
82
+
83
+ _g.selectAll('.arc').data(_data.arcs).enter().append('path')
84
+ .attr('class', 'arc')
85
+ .attr('d', d => {
86
+ const s = _filePositions.get(d.sourceFile);
87
+ const t = _filePositions.get(d.targetFile);
88
+ if (!s || !t) return null;
89
+ const x1 = s.x, x2 = t.x, dist = Math.abs(x2 - x1), midX = (x1 + x2) / 2;
90
+ return `M ${x1} ${baseline} Q ${midX} ${baseline - dist * 0.4} ${x2} ${baseline}`;
91
+ })
92
+ .attr('stroke', d => {
93
+ const s = _filePositions.get(d.sourceFile);
94
+ const t = _filePositions.get(d.targetFile);
95
+ if (!s || !t) return '#4a9eff';
96
+ return d3.interpolateRainbow(Math.abs(t.x - s.x) / maxDistance);
97
+ })
98
+ .attr('stroke-width', d => Math.min(4, 1 + Math.log(d.edgeCount)))
99
+ .on('mouseover', function(event, d) {
100
+ if (_selectedArc) return;
101
+ d3.select(this).classed('highlighted', true);
102
+ ctr.selectAll('.arc').filter(a => a !== d).classed('dimmed', true);
103
+ ctr.selectAll('.file-bar').each(function(f) {
104
+ const match = f.path === d.sourceFile || f.path === d.targetFile;
105
+ d3.select(this).classed('highlighted', match).classed('dimmed', !match);
106
+ });
107
+ _showTooltip(event, `<div class="tooltip-line"><strong>${d.sourceFile}</strong> → <strong>${d.targetFile}</strong></div><div class="tooltip-line"><span class="tooltip-label">Edges:</span> ${d.edgeCount}</div>`);
108
+ })
109
+ .on('mouseout', function() {
110
+ if (_selectedArc) return;
111
+ d3.select(this).classed('highlighted', false);
112
+ ctr.selectAll('.arc').classed('dimmed', false);
113
+ ctr.selectAll('.file-bar').classed('highlighted', false).classed('dimmed', false);
114
+ _hideTooltip();
115
+ })
116
+ .on('click', function(event, d) {
117
+ event.stopPropagation();
118
+ if (_selectedArc === d) { _selectedArc = null; ctr.selectAll('.arc,.file-bar').classed('highlighted', false).classed('dimmed', false); _hideTooltip(); return; }
119
+ _selectedArc = d; _selectedFile = null;
120
+ ctr.selectAll('.arc').classed('highlighted', false).classed('dimmed', true);
121
+ d3.select(this).classed('highlighted', true).classed('dimmed', false);
122
+ ctr.selectAll('.file-bar').each(function(f) {
123
+ const match = f.path === d.sourceFile || f.path === d.targetFile;
124
+ d3.select(this).classed('highlighted', match).classed('dimmed', !match);
125
+ });
126
+ });
127
+
128
+ _g.selectAll('.file-bar').data(_data.files).enter().append('rect')
129
+ .attr('class', 'file-bar')
130
+ .attr('x', d => { const p = _filePositions.get(d.path); return p.x - p.width / 2; })
131
+ .attr('y', baseline).attr('width', d => _filePositions.get(d.path).width).attr('height', 8)
132
+ .attr('fill', d => colorScale(d.directory))
133
+ .on('mouseover', function(event, d) {
134
+ if (_selectedFile) return;
135
+ d3.select(this).classed('highlighted', true);
136
+ const connected = _data.arcs.filter(a => a.sourceFile === d.path || a.targetFile === d.path);
137
+ ctr.selectAll('.arc').classed('highlighted', a => connected.includes(a)).classed('dimmed', a => !connected.includes(a));
138
+ ctr.selectAll('.file-bar').filter(f => f !== d).classed('dimmed', true);
139
+ _showTooltip(event, `<div class="tooltip-line"><strong>${d.path}</strong></div><div class="tooltip-line"><span class="tooltip-label">Symbols:</span> ${d.symbolCount} | In: ${d.incomingCount} | Out: ${d.outgoingCount}</div>`);
140
+ })
141
+ .on('mouseout', function() {
142
+ if (_selectedFile) return;
143
+ d3.select(this).classed('highlighted', false);
144
+ ctr.selectAll('.arc').classed('highlighted', false).classed('dimmed', false);
145
+ ctr.selectAll('.file-bar').classed('dimmed', false);
146
+ _hideTooltip();
147
+ })
148
+ .on('click', function(event, d) {
149
+ event.stopPropagation();
150
+ if (_selectedFile === d) { _selectedFile = null; ctr.selectAll('.arc,.file-bar').classed('highlighted', false).classed('dimmed', false); return; }
151
+ _selectedFile = d; _selectedArc = null;
152
+ const connected = _data.arcs.filter(a => a.sourceFile === d.path || a.targetFile === d.path);
153
+ ctr.selectAll('.arc').classed('highlighted', a => connected.includes(a)).classed('dimmed', a => !connected.includes(a));
154
+ ctr.selectAll('.file-bar').classed('highlighted', f => f === d).classed('dimmed', f => f !== d);
155
+ });
156
+
157
+ _g.selectAll('.file-label').data(_data.files).enter().append('text')
158
+ .attr('class', 'file-label')
159
+ .attr('x', d => _filePositions.get(d.path).x)
160
+ .attr('y', baseline + 20)
161
+ .attr('transform', d => `rotate(-45, ${_filePositions.get(d.path).x}, ${baseline + 20})`)
162
+ .attr('text-anchor', 'end')
163
+ .text(d => d.path.split('/').pop());
164
+
165
+ _svg.append('text').attr('x', 10).attr('y', 20).attr('fill', '#4a9eff')
166
+ .attr('font-size', '12px').attr('cursor', 'pointer').text('↺ Reset View')
167
+ .on('click', () => { _svg.transition().duration(750).call(zoom.transform, d3.zoomIdentity); _clearSelection(); });
168
+ }
169
+
170
+ return { render };
171
+ };
172
+
173
+ // ── Legacy global state and functions (used by depwire viz) ──
2
174
  let graphData = null;
3
175
  let svg = null;
4
176
  let g = null;
@@ -23,12 +195,16 @@ async function init() {
23
195
  // Render diagram
24
196
  renderArcDiagram();
25
197
 
26
- // Setup interactions
27
- setupSearch();
28
- setupExport();
198
+ // Setup interactions (not in whatif mode)
199
+ if (!window.__depwireWhatIf) {
200
+ setupSearch();
201
+ setupExport();
202
+ }
29
203
 
30
- // Setup WebSocket for live updates
31
- setupWebSocket();
204
+ // Setup WebSocket for live updates (not in whatif mode)
205
+ if (!window.__depwireWhatIf) {
206
+ setupWebSocket();
207
+ }
32
208
 
33
209
  // Handle window resize
34
210
  window.addEventListener('resize', () => {
@@ -575,5 +751,7 @@ d3.select('body').on('click', () => {
575
751
  }
576
752
  });
577
753
 
578
- // Initialize on page load
579
- init();
754
+ // Initialize on page load — only if NOT in What If context
755
+ if (!window.__depwireWhatIf) {
756
+ init();
757
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "depwire-cli",
3
- "version": "0.9.24",
3
+ "version": "0.9.26",
4
4
  "description": "Dependency graph + 16 MCP tools for AI coding assistants. Impact analysis, health scoring, visualization.",
5
5
  "type": "module",
6
6
  "bin": {