depwire-cli 1.0.8 → 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/README.md +15 -3
- package/dist/index.js +118 -48
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,6 +29,18 @@
|
|
|
29
29
|
|
|
30
30
|
**Your AI doesn't know your architecture. Depwire does.**
|
|
31
31
|
|
|
32
|
+
## What makes Depwire different
|
|
33
|
+
|
|
34
|
+
<p align="center">
|
|
35
|
+
<img src="./assets/deterministic_vs_rag_diagram.svg" alt="Depwire deterministic graph vs RAG probabilistic approach" width="680" />
|
|
36
|
+
</p>
|
|
37
|
+
|
|
38
|
+
Depwire builds a **DETERMINISTIC, NOT PROBABILISTIC** dependency graph of your codebase. This is not RAG. There are no embeddings, no similarity scores, no vector databases, no guesses. Depwire uses tree-sitter — the same parser powering GitHub's code intelligence — to extract exact symbol-level facts from every file: every function, every class, every interface, every import and export relationship, across 11 programming languages. When you ask "what breaks if I delete `encodeToken` in `auth/token.ts`?", Depwire does not search for similar-looking code and estimate an answer. It traverses the exact dependency graph and returns the precise list of 14 files that import that symbol, which import chains break, and what your health score drops by. This is compiler-level precision applied to AI-assisted development — not a language model's best guess about your code.
|
|
39
|
+
|
|
40
|
+
**Not a build graph either.** Tools like Nx, Turborepo, and Grapher track package-level dependencies for build caching. Depwire tracks symbol-level dependencies — every function, class, and import relationship — which is what makes What If simulation, graph-aware security scanning, and exact blast radius analysis possible.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
32
44
|
Depwire is the infrastructure layer between your AI coding assistant and your codebase. Before your AI touches a single file, Depwire has already mapped every connection, scored every risk, and simulated every change.
|
|
33
45
|
|
|
34
46
|

|
|
@@ -316,11 +328,11 @@ TypeScript, JavaScript, Python, Go, Rust, C, C#, Java, C++, Kotlin, PHP — with
|
|
|
316
328
|
|
|
317
329
|
**C# / .NET** — classes, interfaces, records, structs, enums, delegates, file-scoped namespaces, primary constructors, global usings, .csproj ProjectReference and PackageReference edges, ASP.NET Core cross-language edges (attribute routing + Minimal API).
|
|
318
330
|
|
|
319
|
-
**C++ / Systems** — classes, structs, unions, enums, namespaces, concepts, coroutines, C++20 modules, template support with parameter stripping. CMakeLists.txt, Conan, and vcpkg dependency edge parsing. Crow, Drogon, Pistache, and cpp-httplib cross-language route detection. Dead code detection with vtable and template exclusions. Health score checks: circular includes, missing header guards, god classes, raw pointer fields, missing virtual destructors. Security scanner:
|
|
331
|
+
**C++ / Systems** — classes, structs, unions, enums, namespaces, concepts, coroutines, C++20 modules, template support with parameter stripping. CMakeLists.txt, Conan, and vcpkg dependency edge parsing. Crow, Drogon, Pistache, and cpp-httplib cross-language route detection. Dead code detection with vtable and template exclusions. Health score checks: circular includes, missing header guards, god classes, raw pointer fields, missing virtual destructors. Security scanner: memory safety patterns, format string issues, memory management patterns, OS command execution patterns.
|
|
320
332
|
|
|
321
|
-
**Kotlin / JVM** — classes, data classes, sealed classes, objects, companion objects, value classes, type aliases, extension functions, enum classes, annotation classes. Coroutine awareness: suspend functions, GlobalScope detection, structured concurrency checks. build.gradle.kts, build.gradle, and settings.gradle.kts dependency parsing. Spring Boot, Ktor, Http4k, and Ktor Resources cross-language route detection. Android Retrofit outgoing edge detection. Dead code detection with Android lifecycle and Spring annotation exclusions. Security scanner:
|
|
333
|
+
**Kotlin / JVM** — classes, data classes, sealed classes, objects, companion objects, value classes, type aliases, extension functions, enum classes, annotation classes. Coroutine awareness: suspend functions, GlobalScope detection, structured concurrency checks. build.gradle.kts, build.gradle, and settings.gradle.kts dependency parsing. Spring Boot, Ktor, Http4k, and Ktor Resources cross-language route detection. Android Retrofit outgoing edge detection. Dead code detection with Android lifecycle and Spring annotation exclusions. Security scanner: database query injection patterns, hardcoded credentials, insecure random, not-null assertion abuse, Ktor missing auth blocks.
|
|
322
334
|
|
|
323
|
-
**PHP / Web** — functions, classes, methods, interfaces, traits, enums, namespaces, use statements, require/include dependency edges. Both procedural and OOP styles. Laravel (Route::get/post/put/delete/patch, middleware), Symfony (#[Route(...)]), Slim Framework, and WordPress REST API (register_rest_route) cross-language route detection. Guzzle and file_get_contents HTTP client edge detection. Dead code detection with WordPress hooks, Laravel service providers, Symfony controllers, and magic method exclusions (__construct, __get, __set, __call). Security scanner:
|
|
335
|
+
**PHP / Web** — functions, classes, methods, interfaces, traits, enums, namespaces, use statements, require/include dependency edges. Both procedural and OOP styles. Laravel (Route::get/post/put/delete/patch, middleware), Symfony (#[Route(...)]), Slim Framework, and WordPress REST API (register_rest_route) cross-language route detection. Guzzle and file_get_contents HTTP client edge detection. Dead code detection with WordPress hooks, Laravel service providers, Symfony controllers, and magic method exclusions (__construct, __get, __set, __call). Security scanner: database query injection patterns, dynamic code execution patterns, OS command execution patterns, regex modifier vulnerabilities, unsafe deserialization patterns, unsafe variable extraction patterns, weak hashing for passwords, deprecated crypto libraries, weak PRNG in security contexts, hardcoded credentials.
|
|
324
336
|
|
|
325
337
|
---
|
|
326
338
|
|
package/dist/index.js
CHANGED
|
@@ -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
|
-
.
|
|
583
|
+
.stats-bar {
|
|
563
584
|
background: #0f1729;
|
|
564
585
|
border: 1px solid #2a2a4a;
|
|
565
586
|
border-radius: 8px;
|
|
566
|
-
padding:
|
|
587
|
+
padding: 12px 24px;
|
|
567
588
|
margin: 16px 24px;
|
|
568
589
|
display: flex;
|
|
569
590
|
align-items: center;
|
|
570
|
-
gap:
|
|
591
|
+
gap: 28px;
|
|
571
592
|
flex-wrap: wrap;
|
|
572
593
|
}
|
|
573
|
-
.
|
|
574
|
-
|
|
575
|
-
|
|
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
|
-
.
|
|
582
|
-
|
|
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 -
|
|
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="
|
|
642
|
-
<div class="
|
|
643
|
-
|
|
644
|
-
|
|
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
|
|
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
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
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
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
.
|
|
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>
|