depwire-cli 0.9.19 → 0.9.21
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 +105 -33
- package/dist/{chunk-S3NZMIIU.js → chunk-QHVWDUSX.js} +382 -1874
- package/dist/chunk-XBCQPU63.js +2002 -0
- package/dist/index.js +172 -28
- package/dist/mcpb-entry.js +5 -3
- package/dist/sdk.d.ts +237 -0
- package/dist/sdk.js +32 -0
- package/package.json +16 -4
package/dist/index.js
CHANGED
|
@@ -1,37 +1,40 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
analyzeDeadCode,
|
|
4
|
-
buildGraph,
|
|
5
|
-
calculateHealthScore,
|
|
6
3
|
checkoutCommit,
|
|
7
4
|
createEmptyState,
|
|
8
5
|
createSnapshot,
|
|
9
|
-
findProjectRoot,
|
|
10
|
-
generateDocs,
|
|
11
|
-
getArchitectureSummary,
|
|
12
6
|
getCommitLog,
|
|
13
7
|
getCurrentBranch,
|
|
14
|
-
getHealthTrend,
|
|
15
|
-
getImpact,
|
|
16
8
|
isGitRepo,
|
|
17
9
|
loadSnapshot,
|
|
18
|
-
parseProject,
|
|
19
10
|
popStash,
|
|
20
11
|
prepareVizData,
|
|
21
12
|
restoreOriginal,
|
|
22
13
|
sampleCommits,
|
|
23
14
|
saveSnapshot,
|
|
24
|
-
searchSymbols,
|
|
25
15
|
startMcpServer,
|
|
26
16
|
startVizServer,
|
|
27
17
|
stashChanges,
|
|
28
18
|
updateFileInGraph,
|
|
29
19
|
watchProject
|
|
30
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-XBCQPU63.js";
|
|
21
|
+
import {
|
|
22
|
+
SimulationEngine,
|
|
23
|
+
analyzeDeadCode,
|
|
24
|
+
buildGraph,
|
|
25
|
+
calculateHealthScore,
|
|
26
|
+
findProjectRoot,
|
|
27
|
+
generateDocs,
|
|
28
|
+
getArchitectureSummary,
|
|
29
|
+
getHealthTrend,
|
|
30
|
+
getImpact,
|
|
31
|
+
parseProject,
|
|
32
|
+
searchSymbols
|
|
33
|
+
} from "./chunk-QHVWDUSX.js";
|
|
31
34
|
|
|
32
35
|
// src/index.ts
|
|
33
36
|
import { Command } from "commander";
|
|
34
|
-
import { resolve, dirname as dirname2, join as join3 } from "path";
|
|
37
|
+
import { resolve as resolve2, dirname as dirname2, join as join3 } from "path";
|
|
35
38
|
import { writeFileSync, readFileSync as readFileSync2, existsSync } from "fs";
|
|
36
39
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
37
40
|
|
|
@@ -302,10 +305,10 @@ async function findAvailablePort(startPort) {
|
|
|
302
305
|
const net = await import("net");
|
|
303
306
|
for (let attempt = 0; attempt < 10; attempt++) {
|
|
304
307
|
const testPort = startPort + attempt;
|
|
305
|
-
const isAvailable = await new Promise((
|
|
306
|
-
const server = net.createServer().once("error", () =>
|
|
308
|
+
const isAvailable = await new Promise((resolve3) => {
|
|
309
|
+
const server = net.createServer().once("error", () => resolve3(false)).once("listening", () => {
|
|
307
310
|
server.close();
|
|
308
|
-
|
|
311
|
+
resolve3(true);
|
|
309
312
|
}).listen(testPort, "127.0.0.1");
|
|
310
313
|
});
|
|
311
314
|
if (isAvailable) {
|
|
@@ -346,13 +349,13 @@ async function startTemporalServer(snapshots, projectRoot, preferredPort = 3334)
|
|
|
346
349
|
console.log(" (Could not open browser automatically)");
|
|
347
350
|
});
|
|
348
351
|
});
|
|
349
|
-
await new Promise((
|
|
352
|
+
await new Promise((resolve3, reject) => {
|
|
350
353
|
server.on("error", reject);
|
|
351
354
|
process.on("SIGINT", () => {
|
|
352
355
|
console.log("\n\nShutting down temporal server...");
|
|
353
356
|
server.close(() => {
|
|
354
357
|
console.log("Server stopped");
|
|
355
|
-
|
|
358
|
+
resolve3();
|
|
356
359
|
process.exit(0);
|
|
357
360
|
});
|
|
358
361
|
});
|
|
@@ -496,6 +499,138 @@ async function trackCommand(command, version = "unknown") {
|
|
|
496
499
|
});
|
|
497
500
|
}
|
|
498
501
|
|
|
502
|
+
// src/commands/whatif.ts
|
|
503
|
+
import { resolve } from "path";
|
|
504
|
+
import chalk from "chalk";
|
|
505
|
+
async function whatif(dir, options) {
|
|
506
|
+
if (!options.simulate) {
|
|
507
|
+
console.log("Usage: depwire whatif [dir] --simulate <action> --target <file> [options]");
|
|
508
|
+
console.log("");
|
|
509
|
+
console.log("Actions: move, delete, rename, split, merge");
|
|
510
|
+
console.log("");
|
|
511
|
+
console.log("Run without --simulate to open interactive browser UI (Phase B)");
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
const validActions = ["move", "delete", "rename", "split", "merge"];
|
|
515
|
+
if (!validActions.includes(options.simulate)) {
|
|
516
|
+
console.error(chalk.red(`Invalid action: ${options.simulate}. Must be one of: ${validActions.join(", ")}`));
|
|
517
|
+
process.exit(1);
|
|
518
|
+
}
|
|
519
|
+
if (!options.target) {
|
|
520
|
+
console.error(chalk.red("--target is required for all simulation actions"));
|
|
521
|
+
process.exit(1);
|
|
522
|
+
}
|
|
523
|
+
const action = buildAction(options);
|
|
524
|
+
const projectRoot = dir === "." ? findProjectRoot() : resolve(dir);
|
|
525
|
+
console.log(`Parsing project: ${projectRoot}`);
|
|
526
|
+
const parsedFiles = await parseProject(projectRoot);
|
|
527
|
+
const graph = buildGraph(parsedFiles);
|
|
528
|
+
console.log(`Built graph: ${graph.order} symbols, ${graph.size} edges`);
|
|
529
|
+
console.log("");
|
|
530
|
+
const engine = new SimulationEngine(graph);
|
|
531
|
+
try {
|
|
532
|
+
const result = engine.simulate(action);
|
|
533
|
+
printResult(result);
|
|
534
|
+
} catch (err) {
|
|
535
|
+
console.error(chalk.red(`Simulation failed: ${err.message}`));
|
|
536
|
+
process.exit(1);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
function buildAction(options) {
|
|
540
|
+
const type = options.simulate;
|
|
541
|
+
const target = options.target;
|
|
542
|
+
switch (type) {
|
|
543
|
+
case "move":
|
|
544
|
+
if (!options.destination) {
|
|
545
|
+
console.error(chalk.red("--destination is required for move action"));
|
|
546
|
+
process.exit(1);
|
|
547
|
+
}
|
|
548
|
+
return { type: "move", target, destination: options.destination };
|
|
549
|
+
case "delete":
|
|
550
|
+
return { type: "delete", target };
|
|
551
|
+
case "rename":
|
|
552
|
+
if (!options.newName) {
|
|
553
|
+
console.error(chalk.red("--new-name is required for rename action"));
|
|
554
|
+
process.exit(1);
|
|
555
|
+
}
|
|
556
|
+
return { type: "rename", target, newName: options.newName };
|
|
557
|
+
case "split":
|
|
558
|
+
if (!options.newFile) {
|
|
559
|
+
console.error(chalk.red("--new-file is required for split action"));
|
|
560
|
+
process.exit(1);
|
|
561
|
+
}
|
|
562
|
+
if (!options.symbols) {
|
|
563
|
+
console.error(chalk.red("--symbols is required for split action (comma-separated)"));
|
|
564
|
+
process.exit(1);
|
|
565
|
+
}
|
|
566
|
+
return {
|
|
567
|
+
type: "split",
|
|
568
|
+
target,
|
|
569
|
+
newFile: options.newFile,
|
|
570
|
+
symbols: options.symbols.split(",").map((s) => s.trim())
|
|
571
|
+
};
|
|
572
|
+
case "merge":
|
|
573
|
+
if (!options.source) {
|
|
574
|
+
console.error(chalk.red("--source is required for merge action"));
|
|
575
|
+
process.exit(1);
|
|
576
|
+
}
|
|
577
|
+
return { type: "merge", target, source: options.source };
|
|
578
|
+
default:
|
|
579
|
+
console.error(chalk.red(`Unknown action: ${type}`));
|
|
580
|
+
process.exit(1);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
function printResult(result) {
|
|
584
|
+
const { action, healthDelta, diff } = result;
|
|
585
|
+
const line = "\u2500".repeat(45);
|
|
586
|
+
console.log(chalk.bold("What If Simulation"));
|
|
587
|
+
console.log(chalk.dim(line));
|
|
588
|
+
const actionStr = formatAction(action);
|
|
589
|
+
console.log(`${chalk.bold("Action:")} ${actionStr}`);
|
|
590
|
+
console.log(chalk.dim(line));
|
|
591
|
+
const deltaSign = healthDelta.delta >= 0 ? "+" : "";
|
|
592
|
+
const deltaColor = healthDelta.improved ? chalk.green : healthDelta.delta === 0 ? chalk.yellow : chalk.red;
|
|
593
|
+
const deltaIcon = healthDelta.improved ? "\u2713 improved" : healthDelta.delta === 0 ? "\u2192 unchanged" : "\u2717 degraded";
|
|
594
|
+
console.log(
|
|
595
|
+
`${chalk.bold("Health Score:")} ${healthDelta.before} \u2192 ${healthDelta.after} ${deltaColor(`(${deltaSign}${healthDelta.delta} ${deltaIcon})`)}`
|
|
596
|
+
);
|
|
597
|
+
const changed = healthDelta.dimensionChanges.filter((d) => d.delta !== 0);
|
|
598
|
+
if (changed.length > 0) {
|
|
599
|
+
for (const d of changed) {
|
|
600
|
+
const dSign = d.delta >= 0 ? "+" : "";
|
|
601
|
+
const dColor = d.delta > 0 ? chalk.green : chalk.red;
|
|
602
|
+
console.log(` ${chalk.dim("\u2022")} ${d.name}: ${d.before} \u2192 ${d.after} ${dColor(`(${dSign}${d.delta})`)}`);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
console.log(`${chalk.bold("Affected Nodes:")} ${diff.affectedNodes.length}`);
|
|
606
|
+
console.log(`${chalk.bold("Broken Imports:")} ${diff.brokenImports.length}`);
|
|
607
|
+
if (diff.brokenImports.length > 0) {
|
|
608
|
+
for (const bi of diff.brokenImports) {
|
|
609
|
+
console.log(` ${chalk.yellow("\u2022")} ${bi.file} ${bi.reason}`);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
console.log(
|
|
613
|
+
`${chalk.bold("Circular Deps:")} ${diff.circularDepsIntroduced.length} introduced, ${diff.circularDepsResolved.length} resolved`
|
|
614
|
+
);
|
|
615
|
+
console.log(`${chalk.bold("Added Edges:")} ${diff.addedEdges.length}`);
|
|
616
|
+
console.log(`${chalk.bold("Removed Edges:")} ${diff.removedEdges.length}`);
|
|
617
|
+
console.log(chalk.dim(line));
|
|
618
|
+
}
|
|
619
|
+
function formatAction(action) {
|
|
620
|
+
switch (action.type) {
|
|
621
|
+
case "move":
|
|
622
|
+
return `MOVE ${action.target} \u2192 ${action.destination}`;
|
|
623
|
+
case "delete":
|
|
624
|
+
return `DELETE ${action.target}`;
|
|
625
|
+
case "rename":
|
|
626
|
+
return `RENAME ${action.target} \u2192 ${action.newName}`;
|
|
627
|
+
case "split":
|
|
628
|
+
return `SPLIT ${action.target} \u2192 ${action.newFile} (${action.symbols.join(", ")})`;
|
|
629
|
+
case "merge":
|
|
630
|
+
return `MERGE ${action.source} \u2192 ${action.target}`;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
499
634
|
// src/index.ts
|
|
500
635
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
501
636
|
var __dirname2 = dirname2(__filename2);
|
|
@@ -507,7 +642,7 @@ program.command("parse").description("Parse a TypeScript project and build depen
|
|
|
507
642
|
trackCommand("parse", packageJson.version);
|
|
508
643
|
const startTime = Date.now();
|
|
509
644
|
try {
|
|
510
|
-
const projectRoot = directory ?
|
|
645
|
+
const projectRoot = directory ? resolve2(directory) : findProjectRoot();
|
|
511
646
|
console.log(`Parsing project: ${projectRoot}`);
|
|
512
647
|
const parsedFiles = await parseProject(projectRoot, {
|
|
513
648
|
exclude: options.exclude,
|
|
@@ -546,7 +681,7 @@ Orphan Files (no cross-references): ${summary.orphanFiles.length}`);
|
|
|
546
681
|
program.command("query").description("Query impact analysis for a symbol").argument("<directory>", "Project directory").argument("<symbol-name>", "Symbol name to query").action(async (directory, symbolName) => {
|
|
547
682
|
trackCommand("query", packageJson.version);
|
|
548
683
|
try {
|
|
549
|
-
const projectRoot =
|
|
684
|
+
const projectRoot = resolve2(directory);
|
|
550
685
|
const cacheFile = "depwire-output.json";
|
|
551
686
|
let graph;
|
|
552
687
|
if (existsSync(cacheFile)) {
|
|
@@ -595,7 +730,7 @@ Total Transitive Dependents: ${impact.transitiveDependents.length}`);
|
|
|
595
730
|
program.command("viz").description("Launch interactive arc diagram visualization").argument("[directory]", "Project directory to visualize (defaults to current directory or auto-detected project root)").option("-p, --port <number>", "Server port", "3333").option("--no-open", "Don't auto-open browser").option("--exclude <patterns...>", 'Glob patterns to exclude (e.g., "**/*.test.*" "dist/**")').option("--verbose", "Show detailed parsing progress").action(async (directory, options) => {
|
|
596
731
|
trackCommand("viz", packageJson.version);
|
|
597
732
|
try {
|
|
598
|
-
const projectRoot = directory ?
|
|
733
|
+
const projectRoot = directory ? resolve2(directory) : findProjectRoot();
|
|
599
734
|
console.log(`Parsing project: ${projectRoot}`);
|
|
600
735
|
const parsedFiles = await parseProject(projectRoot, {
|
|
601
736
|
exclude: options.exclude,
|
|
@@ -618,7 +753,7 @@ program.command("viz").description("Launch interactive arc diagram visualization
|
|
|
618
753
|
program.command("temporal").description("Visualize how the dependency graph evolved over git history").argument("[directory]", "Project directory to analyze (defaults to current directory or auto-detected project root)").option("--commits <number>", "Number of commits to sample", "20").option("--strategy <type>", "Sampling strategy: even, weekly, monthly", "even").option("-p, --port <number>", "Server port", "3334").option("--output <path>", "Save snapshots to custom path (default: .depwire/temporal/)").option("--verbose", "Show progress for each commit being parsed").option("--stats", "Show summary statistics at end").action(async (directory, options) => {
|
|
619
754
|
trackCommand("temporal", packageJson.version);
|
|
620
755
|
try {
|
|
621
|
-
const projectRoot = directory ?
|
|
756
|
+
const projectRoot = directory ? resolve2(directory) : findProjectRoot();
|
|
622
757
|
await runTemporalAnalysis(projectRoot, {
|
|
623
758
|
commits: parseInt(options.commits, 10),
|
|
624
759
|
strategy: options.strategy,
|
|
@@ -638,7 +773,7 @@ program.command("mcp").description("Start MCP server for AI coding tools").argum
|
|
|
638
773
|
const state = createEmptyState();
|
|
639
774
|
let projectRootToConnect = null;
|
|
640
775
|
if (directory) {
|
|
641
|
-
projectRootToConnect =
|
|
776
|
+
projectRootToConnect = resolve2(directory);
|
|
642
777
|
} else {
|
|
643
778
|
const detectedRoot = findProjectRoot();
|
|
644
779
|
const cwd = process.cwd();
|
|
@@ -699,8 +834,8 @@ program.command("docs").description("Generate comprehensive codebase documentati
|
|
|
699
834
|
trackCommand("docs", packageJson.version);
|
|
700
835
|
const startTime = Date.now();
|
|
701
836
|
try {
|
|
702
|
-
const projectRoot = directory ?
|
|
703
|
-
const outputDir = options.output ?
|
|
837
|
+
const projectRoot = directory ? resolve2(directory) : findProjectRoot();
|
|
838
|
+
const outputDir = options.output ? resolve2(options.output) : join3(projectRoot, ".depwire");
|
|
704
839
|
const includeList = options.include.split(",").map((s) => s.trim());
|
|
705
840
|
const onlyList = options.only ? options.only.split(",").map((s) => s.trim()) : void 0;
|
|
706
841
|
if (options.gitignore === void 0 && !existsSyncNode(outputDir)) {
|
|
@@ -762,11 +897,11 @@ async function promptGitignore() {
|
|
|
762
897
|
input: process.stdin,
|
|
763
898
|
output: process.stdout
|
|
764
899
|
});
|
|
765
|
-
return new Promise((
|
|
900
|
+
return new Promise((resolve3) => {
|
|
766
901
|
rl.question("Add .depwire/ to .gitignore? [Y/n] ", (answer) => {
|
|
767
902
|
rl.close();
|
|
768
903
|
const normalized = answer.trim().toLowerCase();
|
|
769
|
-
|
|
904
|
+
resolve3(normalized === "" || normalized === "y" || normalized === "yes");
|
|
770
905
|
});
|
|
771
906
|
});
|
|
772
907
|
}
|
|
@@ -796,7 +931,7 @@ ${pattern}
|
|
|
796
931
|
program.command("health").description("Analyze dependency architecture health (0-100 score)").argument("[directory]", "Project directory to analyze (defaults to current directory or auto-detected project root)").option("--json", "Output as JSON").option("--verbose", "Show detailed breakdown").action(async (directory, options) => {
|
|
797
932
|
trackCommand("health", packageJson.version);
|
|
798
933
|
try {
|
|
799
|
-
const projectRoot = directory ?
|
|
934
|
+
const projectRoot = directory ? resolve2(directory) : findProjectRoot();
|
|
800
935
|
const startTime = Date.now();
|
|
801
936
|
const parsedFiles = await parseProject(projectRoot);
|
|
802
937
|
const graph = buildGraph(parsedFiles);
|
|
@@ -820,7 +955,7 @@ program.command("health").description("Analyze dependency architecture health (0
|
|
|
820
955
|
program.command("dead-code").description("Identify dead code - symbols defined but never referenced").argument("[directory]", "Project directory to analyze (defaults to current directory or auto-detected project root)").option("--confidence <level>", "Minimum confidence level to show: high, medium, low (default: medium)", "medium").option("--json", "Output as JSON (for CI/automation)").option("--verbose", "Show detailed info for each dead symbol").option("--stats", "Show summary statistics").option("--include-tests", "Include test files in analysis").option("--include-low", "Shortcut for --confidence low").option("--debug", "Show debug information (exclusion stats)").action(async (directory, options) => {
|
|
821
956
|
trackCommand("dead-code", packageJson.version);
|
|
822
957
|
try {
|
|
823
|
-
const projectRoot = directory ?
|
|
958
|
+
const projectRoot = directory ? resolve2(directory) : findProjectRoot();
|
|
824
959
|
const startTime = Date.now();
|
|
825
960
|
const parsedFiles = await parseProject(projectRoot);
|
|
826
961
|
const graph = buildGraph(parsedFiles);
|
|
@@ -847,4 +982,13 @@ Analysis completed in ${(totalTime / 1e3).toFixed(2)}s
|
|
|
847
982
|
process.exit(1);
|
|
848
983
|
}
|
|
849
984
|
});
|
|
985
|
+
program.command("whatif").description("Simulate architectural changes before touching code").argument("[directory]", "Project directory (defaults to auto-detected project root)").option("--simulate <action>", "Action to simulate: move, delete, rename, split, merge").option("--target <file>", "File to apply the action to").option("--destination <file>", "Destination path (for move action)").option("--new-name <name>", "New name (for rename action)").option("--source <file>", "Source file (for merge action)").option("--new-file <file>", "New file path (for split action)").option("--symbols <symbols>", "Comma-separated symbol names (for split action)").action(async (directory, options) => {
|
|
986
|
+
trackCommand("whatif", packageJson.version);
|
|
987
|
+
try {
|
|
988
|
+
await whatif(directory || ".", options);
|
|
989
|
+
} catch (err) {
|
|
990
|
+
console.error("Error running simulation:", err);
|
|
991
|
+
process.exit(1);
|
|
992
|
+
}
|
|
993
|
+
});
|
|
850
994
|
program.parse();
|
package/dist/mcpb-entry.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
buildGraph,
|
|
4
3
|
createEmptyState,
|
|
5
|
-
parseProject,
|
|
6
4
|
startMcpServer,
|
|
7
5
|
updateFileInGraph,
|
|
8
6
|
watchProject
|
|
9
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-XBCQPU63.js";
|
|
8
|
+
import {
|
|
9
|
+
buildGraph,
|
|
10
|
+
parseProject
|
|
11
|
+
} from "./chunk-QHVWDUSX.js";
|
|
10
12
|
|
|
11
13
|
// src/mcpb-entry.ts
|
|
12
14
|
import { resolve } from "path";
|
package/dist/sdk.d.ts
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { DirectedGraph } from 'graphology';
|
|
2
|
+
|
|
3
|
+
type SymbolKind = 'function' | 'class' | 'variable' | 'constant' | 'type_alias' | 'interface' | 'enum' | 'import' | 'export' | 'method' | 'property' | 'decorator' | 'module';
|
|
4
|
+
interface SymbolNode {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
kind: SymbolKind;
|
|
8
|
+
filePath: string;
|
|
9
|
+
startLine: number;
|
|
10
|
+
endLine: number;
|
|
11
|
+
exported: boolean;
|
|
12
|
+
scope?: string;
|
|
13
|
+
}
|
|
14
|
+
type EdgeKind = 'imports' | 'calls' | 'extends' | 'implements' | 'inherits' | 'decorates' | 'references' | 'type_references';
|
|
15
|
+
interface SymbolEdge {
|
|
16
|
+
source: string;
|
|
17
|
+
target: string;
|
|
18
|
+
kind: EdgeKind;
|
|
19
|
+
filePath: string;
|
|
20
|
+
line: number;
|
|
21
|
+
}
|
|
22
|
+
interface ParsedFile {
|
|
23
|
+
filePath: string;
|
|
24
|
+
symbols: SymbolNode[];
|
|
25
|
+
edges: SymbolEdge[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* SECURITY: All parser operations are READ-ONLY.
|
|
30
|
+
* Depwire never writes to, modifies, or deletes any file in the user's project.
|
|
31
|
+
* The only file system writes are to os.tmpdir() for cloned repos.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
declare function parseProject(projectRoot: string, options?: {
|
|
35
|
+
exclude?: string[];
|
|
36
|
+
verbose?: boolean;
|
|
37
|
+
}): Promise<ParsedFile[]>;
|
|
38
|
+
|
|
39
|
+
declare function buildGraph(parsedFiles: ParsedFile[]): DirectedGraph;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Health Score Type Definitions
|
|
43
|
+
*/
|
|
44
|
+
interface HealthDimension {
|
|
45
|
+
name: string;
|
|
46
|
+
score: number;
|
|
47
|
+
weight: number;
|
|
48
|
+
grade: string;
|
|
49
|
+
details: string;
|
|
50
|
+
metrics: Record<string, number | string>;
|
|
51
|
+
}
|
|
52
|
+
interface HealthReport {
|
|
53
|
+
overall: number;
|
|
54
|
+
grade: string;
|
|
55
|
+
dimensions: HealthDimension[];
|
|
56
|
+
summary: string;
|
|
57
|
+
recommendations: string[];
|
|
58
|
+
projectStats: {
|
|
59
|
+
files: number;
|
|
60
|
+
symbols: number;
|
|
61
|
+
edges: number;
|
|
62
|
+
languages: Record<string, number>;
|
|
63
|
+
};
|
|
64
|
+
timestamp: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Calculate the overall health score for a project
|
|
69
|
+
*/
|
|
70
|
+
declare function calculateHealthScore(graph: DirectedGraph, projectRoot: string): HealthReport;
|
|
71
|
+
|
|
72
|
+
type ConfidenceLevel = "high" | "medium" | "low";
|
|
73
|
+
interface DeadSymbol {
|
|
74
|
+
name: string;
|
|
75
|
+
kind: string;
|
|
76
|
+
file: string;
|
|
77
|
+
line: number;
|
|
78
|
+
exported: boolean;
|
|
79
|
+
dependents: number;
|
|
80
|
+
confidence: ConfidenceLevel;
|
|
81
|
+
reason: string;
|
|
82
|
+
}
|
|
83
|
+
interface DeadCodeReport {
|
|
84
|
+
totalSymbols: number;
|
|
85
|
+
deadSymbols: number;
|
|
86
|
+
deadPercentage: number;
|
|
87
|
+
byConfidence: {
|
|
88
|
+
high: number;
|
|
89
|
+
medium: number;
|
|
90
|
+
low: number;
|
|
91
|
+
};
|
|
92
|
+
symbols: DeadSymbol[];
|
|
93
|
+
}
|
|
94
|
+
interface DeadCodeOptions {
|
|
95
|
+
confidence: ConfidenceLevel;
|
|
96
|
+
includeTests: boolean;
|
|
97
|
+
verbose: boolean;
|
|
98
|
+
stats: boolean;
|
|
99
|
+
json: boolean;
|
|
100
|
+
debug: boolean;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
declare function analyzeDeadCode(graph: DirectedGraph, projectRoot: string, options?: Partial<DeadCodeOptions>): DeadCodeReport;
|
|
104
|
+
|
|
105
|
+
interface GeneratorOptions {
|
|
106
|
+
outputDir: string;
|
|
107
|
+
format: 'markdown' | 'json';
|
|
108
|
+
include: string[];
|
|
109
|
+
update: boolean;
|
|
110
|
+
only?: string[];
|
|
111
|
+
verbose: boolean;
|
|
112
|
+
stats: boolean;
|
|
113
|
+
}
|
|
114
|
+
interface GenerationResult {
|
|
115
|
+
success: boolean;
|
|
116
|
+
generated: string[];
|
|
117
|
+
errors: string[];
|
|
118
|
+
stats?: {
|
|
119
|
+
totalTime: number;
|
|
120
|
+
filesGenerated: number;
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Main documentation generator
|
|
125
|
+
*/
|
|
126
|
+
declare function generateDocs(graph: DirectedGraph, projectRoot: string, version: string, parseTime: number, options: GeneratorOptions): Promise<GenerationResult>;
|
|
127
|
+
|
|
128
|
+
declare function getImpact(graph: DirectedGraph, symbolId: string): {
|
|
129
|
+
directDependents: SymbolNode[];
|
|
130
|
+
transitiveDependents: SymbolNode[];
|
|
131
|
+
affectedFiles: string[];
|
|
132
|
+
};
|
|
133
|
+
declare function searchSymbols(graph: DirectedGraph, query: string): SymbolNode[];
|
|
134
|
+
declare function getArchitectureSummary(graph: DirectedGraph): {
|
|
135
|
+
fileCount: number;
|
|
136
|
+
symbolCount: number;
|
|
137
|
+
edgeCount: number;
|
|
138
|
+
mostConnectedFiles: {
|
|
139
|
+
filePath: string;
|
|
140
|
+
connections: number;
|
|
141
|
+
}[];
|
|
142
|
+
orphanFiles: string[];
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
type SimulationAction = {
|
|
146
|
+
type: 'move';
|
|
147
|
+
target: string;
|
|
148
|
+
destination: string;
|
|
149
|
+
} | {
|
|
150
|
+
type: 'delete';
|
|
151
|
+
target: string;
|
|
152
|
+
} | {
|
|
153
|
+
type: 'rename';
|
|
154
|
+
target: string;
|
|
155
|
+
newName: string;
|
|
156
|
+
} | {
|
|
157
|
+
type: 'split';
|
|
158
|
+
target: string;
|
|
159
|
+
newFile: string;
|
|
160
|
+
symbols: string[];
|
|
161
|
+
} | {
|
|
162
|
+
type: 'merge';
|
|
163
|
+
target: string;
|
|
164
|
+
source: string;
|
|
165
|
+
};
|
|
166
|
+
interface SimulationResult {
|
|
167
|
+
action: SimulationAction;
|
|
168
|
+
originalGraph: GraphSnapshot;
|
|
169
|
+
simulatedGraph: GraphSnapshot;
|
|
170
|
+
diff: GraphDiff;
|
|
171
|
+
healthDelta: HealthDelta;
|
|
172
|
+
}
|
|
173
|
+
interface GraphSnapshot {
|
|
174
|
+
nodeCount: number;
|
|
175
|
+
edgeCount: number;
|
|
176
|
+
healthScore: number;
|
|
177
|
+
}
|
|
178
|
+
interface GraphDiff {
|
|
179
|
+
addedEdges: EdgeInfo[];
|
|
180
|
+
removedEdges: EdgeInfo[];
|
|
181
|
+
affectedNodes: string[];
|
|
182
|
+
brokenImports: BrokenImport[];
|
|
183
|
+
circularDepsIntroduced: string[][];
|
|
184
|
+
circularDepsResolved: string[][];
|
|
185
|
+
}
|
|
186
|
+
interface HealthDelta {
|
|
187
|
+
before: number;
|
|
188
|
+
after: number;
|
|
189
|
+
delta: number;
|
|
190
|
+
improved: boolean;
|
|
191
|
+
dimensionChanges: DimensionChange[];
|
|
192
|
+
}
|
|
193
|
+
interface DimensionChange {
|
|
194
|
+
name: string;
|
|
195
|
+
before: number;
|
|
196
|
+
after: number;
|
|
197
|
+
delta: number;
|
|
198
|
+
}
|
|
199
|
+
interface BrokenImport {
|
|
200
|
+
file: string;
|
|
201
|
+
importedSymbol: string;
|
|
202
|
+
reason: string;
|
|
203
|
+
}
|
|
204
|
+
interface EdgeInfo {
|
|
205
|
+
source: string;
|
|
206
|
+
target: string;
|
|
207
|
+
kind?: string;
|
|
208
|
+
}
|
|
209
|
+
declare class SimulationEngine {
|
|
210
|
+
private readonly original;
|
|
211
|
+
constructor(graph: DirectedGraph);
|
|
212
|
+
simulate(action: SimulationAction): SimulationResult;
|
|
213
|
+
private applyMove;
|
|
214
|
+
private applyDelete;
|
|
215
|
+
private applyRename;
|
|
216
|
+
private applySplit;
|
|
217
|
+
private applyMerge;
|
|
218
|
+
private computeDiff;
|
|
219
|
+
private collectEdges;
|
|
220
|
+
private edgeKey;
|
|
221
|
+
private detectCycles;
|
|
222
|
+
private computeHealthScore;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* depwire-cli SDK — Public API Surface
|
|
227
|
+
*
|
|
228
|
+
* This is the ONLY file the cloud (Railway parser) should import from.
|
|
229
|
+
* Never import from internal paths like depwire-cli/dist/graph/index.js.
|
|
230
|
+
*
|
|
231
|
+
* Rule: if the cloud needs something not exported here, add it here —
|
|
232
|
+
* do not reach into internal paths.
|
|
233
|
+
*/
|
|
234
|
+
/** Current SDK version — matches depwire-cli npm version */
|
|
235
|
+
declare const DepwireSDKVersion: string;
|
|
236
|
+
|
|
237
|
+
export { type BrokenImport, DepwireSDKVersion, type GraphDiff, type HealthDelta, type SimulationAction, SimulationEngine, type SimulationResult, analyzeDeadCode, buildGraph, calculateHealthScore, generateDocs, getArchitectureSummary, getImpact, parseProject, searchSymbols };
|
package/dist/sdk.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SimulationEngine,
|
|
3
|
+
analyzeDeadCode,
|
|
4
|
+
buildGraph,
|
|
5
|
+
calculateHealthScore,
|
|
6
|
+
generateDocs,
|
|
7
|
+
getArchitectureSummary,
|
|
8
|
+
getImpact,
|
|
9
|
+
parseProject,
|
|
10
|
+
searchSymbols
|
|
11
|
+
} from "./chunk-QHVWDUSX.js";
|
|
12
|
+
|
|
13
|
+
// src/sdk.ts
|
|
14
|
+
import { readFileSync } from "fs";
|
|
15
|
+
import { dirname, join } from "path";
|
|
16
|
+
import { fileURLToPath } from "url";
|
|
17
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
18
|
+
var __dirname = dirname(__filename);
|
|
19
|
+
var packageJson = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
|
|
20
|
+
var DepwireSDKVersion = packageJson.version;
|
|
21
|
+
export {
|
|
22
|
+
DepwireSDKVersion,
|
|
23
|
+
SimulationEngine,
|
|
24
|
+
analyzeDeadCode,
|
|
25
|
+
buildGraph,
|
|
26
|
+
calculateHealthScore,
|
|
27
|
+
generateDocs,
|
|
28
|
+
getArchitectureSummary,
|
|
29
|
+
getImpact,
|
|
30
|
+
parseProject,
|
|
31
|
+
searchSymbols
|
|
32
|
+
};
|
package/package.json
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "depwire-cli",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.21",
|
|
4
4
|
"description": "The missing context layer for AI coding assistants. Dependency graph, MCP server, architecture health, dead code detection.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"depwire": "dist/index.js"
|
|
8
8
|
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./dist/index.js",
|
|
11
|
+
"./sdk": "./dist/sdk.js"
|
|
12
|
+
},
|
|
9
13
|
"scripts": {
|
|
10
|
-
"build": "tsup src/index.ts src/mcpb-entry.ts --format esm --dts --clean && npm run copy-static",
|
|
14
|
+
"build": "tsup src/index.ts src/mcpb-entry.ts src/sdk.ts --format esm --dts --clean && npm run copy-static",
|
|
11
15
|
"copy-static": "mkdir -p dist/viz/public dist/parser/grammars && cp -r src/viz/public/* dist/viz/public/ && cp src/parser/grammars/*.wasm dist/parser/grammars/",
|
|
12
16
|
"dev": "tsup src/index.ts --format esm --watch",
|
|
13
17
|
"start": "node dist/index.js",
|
|
14
|
-
"build:mcpb": "npm run build && ./scripts/build-mcpb.sh"
|
|
18
|
+
"build:mcpb": "npm run build && ./scripts/build-mcpb.sh",
|
|
19
|
+
"postversion": "node -e \"const fs=require('fs');const p=JSON.parse(fs.readFileSync('./package.json','utf8'));const s=JSON.parse(fs.readFileSync('./server.json','utf8'));s.version=p.version;s.packages[0].version=p.version;fs.writeFileSync('./server.json',JSON.stringify(s,null,2)+'\\n');console.log('server.json updated to '+p.version);\" && git add server.json"
|
|
15
20
|
},
|
|
16
21
|
"keywords": [
|
|
17
22
|
"typescript",
|
|
@@ -28,7 +33,14 @@
|
|
|
28
33
|
"impact-analysis",
|
|
29
34
|
"refactoring",
|
|
30
35
|
"arc-diagram",
|
|
31
|
-
"cross-reference"
|
|
36
|
+
"cross-reference",
|
|
37
|
+
"whatif",
|
|
38
|
+
"simulation",
|
|
39
|
+
"architectural-simulation",
|
|
40
|
+
"what-if-analysis",
|
|
41
|
+
"refactor-simulation",
|
|
42
|
+
"blast-radius",
|
|
43
|
+
"deterministic-graph"
|
|
32
44
|
],
|
|
33
45
|
"author": "Atef Ataya (https://www.youtube.com/@atefataya)",
|
|
34
46
|
"license": "BUSL-1.1",
|