depwire-cli 0.9.1 → 0.9.2

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 CHANGED
@@ -1,11 +1,5 @@
1
1
  # Depwire
2
2
 
3
- <p align="center">
4
- <a href="https://glama.ai/mcp/servers/depwire/depwire">
5
- <img width="380" height="200" src="https://glama.ai/mcp/servers/depwire/depwire/badge" alt="Depwire MCP server" />
6
- </a>
7
- </p>
8
-
9
3
  <p align="center">
10
4
  <a href="https://www.npmjs.com/package/depwire-cli">
11
5
  <img src="https://img.shields.io/npm/v/depwire-cli.svg?style=flat-square" alt="npm version" />
@@ -3987,21 +3987,89 @@ function loadHealthHistory(projectRoot) {
3987
3987
 
3988
3988
  // src/dead-code/detector.ts
3989
3989
  import path2 from "path";
3990
- function findDeadSymbols(graph, projectRoot, includeTests = false) {
3990
+ import { readFileSync as readFileSync7, existsSync as existsSync9 } from "fs";
3991
+ function findDeadSymbols(graph, projectRoot, includeTests = false, debug = false) {
3991
3992
  const deadSymbols = [];
3992
3993
  const context = { graph, projectRoot };
3994
+ const stats = {
3995
+ total: 0,
3996
+ excludedByTestFile: 0,
3997
+ excludedByEntryPoint: 0,
3998
+ excludedByConfigFile: 0,
3999
+ excludedByTypeDeclaration: 0,
4000
+ excludedByDefaultExport: 0,
4001
+ excludedByFrameworkDir: 0
4002
+ };
4003
+ const packageEntryPoints = getPackageEntryPoints(projectRoot);
4004
+ if (debug) {
4005
+ console.log("\n\u{1F50D} Debug: Graph Structure");
4006
+ console.log(`Total nodes in graph: ${graph.order}`);
4007
+ console.log(`Total edges in graph: ${graph.size}`);
4008
+ let nodesWithZeroInDegree = 0;
4009
+ let nodesWithZeroOutDegree = 0;
4010
+ graph.forEachNode((node) => {
4011
+ if (graph.inDegree(node) === 0) nodesWithZeroInDegree++;
4012
+ if (graph.outDegree(node) === 0) nodesWithZeroOutDegree++;
4013
+ });
4014
+ console.log(`Nodes with inDegree=0: ${nodesWithZeroInDegree}`);
4015
+ console.log(`Nodes with outDegree=0: ${nodesWithZeroOutDegree}`);
4016
+ if (nodesWithZeroInDegree <= 10) {
4017
+ console.log("\nSample nodes with inDegree=0:");
4018
+ let count = 0;
4019
+ graph.forEachNode((node) => {
4020
+ if (graph.inDegree(node) === 0 && count < 10) {
4021
+ const attrs = graph.getNodeAttributes(node);
4022
+ const filePath = attrs.file || attrs.filePath || "unknown";
4023
+ console.log(` - ${attrs.name} (${attrs.kind}) in ${path2.relative(projectRoot, filePath)}`);
4024
+ count++;
4025
+ }
4026
+ });
4027
+ }
4028
+ }
3993
4029
  for (const node of graph.nodes()) {
3994
4030
  const attrs = graph.getNodeAttributes(node);
3995
- if (!attrs.file || !attrs.name) continue;
4031
+ if (!attrs.name) continue;
4032
+ if (!attrs.file && !attrs.filePath) {
4033
+ if (debug) {
4034
+ console.log(`Skipping node ${attrs.name} - no file attribute`);
4035
+ }
4036
+ continue;
4037
+ }
4038
+ const filePath = attrs.file || attrs.filePath;
4039
+ if (!isRelevantForDeadCodeDetection(attrs)) {
4040
+ continue;
4041
+ }
3996
4042
  const inDegree = graph.inDegree(node);
3997
4043
  if (inDegree === 0) {
3998
- if (shouldExclude(attrs, context, includeTests)) {
4044
+ stats.total++;
4045
+ const exclusionReason = shouldExclude(attrs, context, includeTests, packageEntryPoints);
4046
+ if (exclusionReason) {
4047
+ switch (exclusionReason) {
4048
+ case "test":
4049
+ stats.excludedByTestFile++;
4050
+ break;
4051
+ case "entry":
4052
+ stats.excludedByEntryPoint++;
4053
+ break;
4054
+ case "config":
4055
+ stats.excludedByConfigFile++;
4056
+ break;
4057
+ case "types":
4058
+ stats.excludedByTypeDeclaration++;
4059
+ break;
4060
+ case "default":
4061
+ stats.excludedByDefaultExport++;
4062
+ break;
4063
+ case "framework":
4064
+ stats.excludedByFrameworkDir++;
4065
+ break;
4066
+ }
3999
4067
  continue;
4000
4068
  }
4001
4069
  deadSymbols.push({
4002
4070
  name: attrs.name,
4003
4071
  kind: attrs.kind || "unknown",
4004
- file: attrs.file,
4072
+ file: filePath,
4005
4073
  line: attrs.startLine || 0,
4006
4074
  exported: attrs.exported || false,
4007
4075
  dependents: 0,
@@ -4010,38 +4078,115 @@ function findDeadSymbols(graph, projectRoot, includeTests = false) {
4010
4078
  });
4011
4079
  }
4012
4080
  }
4013
- return deadSymbols;
4081
+ if (debug) {
4082
+ console.log("\n\u{1F50D} Debug: Exclusion Statistics");
4083
+ console.log(`Total symbols with 0 incoming edges: ${stats.total}`);
4084
+ console.log(`Excluded by test file: ${stats.excludedByTestFile}`);
4085
+ console.log(`Excluded by entry point: ${stats.excludedByEntryPoint}`);
4086
+ console.log(`Excluded by config file: ${stats.excludedByConfigFile}`);
4087
+ console.log(`Excluded by type declaration: ${stats.excludedByTypeDeclaration}`);
4088
+ console.log(`Excluded by default export: ${stats.excludedByDefaultExport}`);
4089
+ console.log(`Excluded by framework dir: ${stats.excludedByFrameworkDir}`);
4090
+ console.log(`Remaining dead symbols: ${deadSymbols.length}
4091
+ `);
4092
+ }
4093
+ return { symbols: deadSymbols, stats };
4094
+ }
4095
+ function isRelevantForDeadCodeDetection(attrs) {
4096
+ const kind = attrs.kind;
4097
+ const relevantKinds = [
4098
+ "function",
4099
+ "class",
4100
+ "interface",
4101
+ "type",
4102
+ "enum",
4103
+ "const",
4104
+ "let",
4105
+ "var",
4106
+ "method",
4107
+ "property"
4108
+ ];
4109
+ if (!relevantKinds.includes(kind)) {
4110
+ return false;
4111
+ }
4112
+ if (kind === "const" || kind === "let" || kind === "var" || kind === "variable") {
4113
+ return attrs.exported === true;
4114
+ }
4115
+ return true;
4116
+ }
4117
+ function getPackageEntryPoints(projectRoot) {
4118
+ const entryPoints = /* @__PURE__ */ new Set();
4119
+ const packageJsonPath = path2.join(projectRoot, "package.json");
4120
+ if (!existsSync9(packageJsonPath)) {
4121
+ return entryPoints;
4122
+ }
4123
+ try {
4124
+ const packageJson = JSON.parse(readFileSync7(packageJsonPath, "utf-8"));
4125
+ if (packageJson.main) {
4126
+ entryPoints.add(path2.resolve(projectRoot, packageJson.main));
4127
+ }
4128
+ if (packageJson.module) {
4129
+ entryPoints.add(path2.resolve(projectRoot, packageJson.module));
4130
+ }
4131
+ if (packageJson.exports) {
4132
+ const addExports = (exp) => {
4133
+ if (typeof exp === "string") {
4134
+ entryPoints.add(path2.resolve(projectRoot, exp));
4135
+ } else if (typeof exp === "object") {
4136
+ for (const key in exp) {
4137
+ if (typeof exp[key] === "string") {
4138
+ entryPoints.add(path2.resolve(projectRoot, exp[key]));
4139
+ } else if (typeof exp[key] === "object") {
4140
+ addExports(exp[key]);
4141
+ }
4142
+ }
4143
+ }
4144
+ };
4145
+ addExports(packageJson.exports);
4146
+ }
4147
+ } catch (e) {
4148
+ }
4149
+ return entryPoints;
4014
4150
  }
4015
- function shouldExclude(attrs, context, includeTests) {
4016
- const filePath = attrs.file;
4151
+ function shouldExclude(attrs, context, includeTests, packageEntryPoints) {
4152
+ const filePath = attrs.file || attrs.filePath;
4153
+ if (!filePath) {
4154
+ return null;
4155
+ }
4017
4156
  const relativePath = path2.relative(context.projectRoot, filePath);
4018
4157
  if (!includeTests && isTestFile(relativePath)) {
4019
- return true;
4158
+ return "test";
4020
4159
  }
4021
- if (isEntryPoint(relativePath)) {
4022
- return true;
4160
+ if (isRealPackageEntryPoint(filePath, packageEntryPoints)) {
4161
+ return "entry";
4023
4162
  }
4024
4163
  if (isConfigFile(relativePath)) {
4025
- return true;
4164
+ return "config";
4026
4165
  }
4027
4166
  if (isTypeDeclarationFile(relativePath)) {
4028
- return true;
4167
+ return "types";
4029
4168
  }
4030
4169
  if (attrs.kind === "default") {
4031
- return true;
4170
+ return "default";
4032
4171
  }
4033
4172
  if (isFrameworkAutoLoadedFile(relativePath)) {
4034
- return true;
4173
+ return "framework";
4174
+ }
4175
+ return null;
4176
+ }
4177
+ function isRealPackageEntryPoint(filePath, packageEntryPoints) {
4178
+ const normalizedPath = path2.normalize(filePath);
4179
+ for (const entryPoint of packageEntryPoints) {
4180
+ const normalizedEntry = path2.normalize(entryPoint);
4181
+ if (normalizedPath === normalizedEntry || normalizedPath === normalizedEntry.replace(/\.(js|ts)$/, ".ts") || normalizedPath === normalizedEntry.replace(/\.(js|ts)$/, ".js")) {
4182
+ return true;
4183
+ }
4035
4184
  }
4036
4185
  return false;
4037
4186
  }
4038
4187
  function isTestFile(filePath) {
4039
4188
  return filePath.includes("__tests__/") || filePath.includes(".test.") || filePath.includes(".spec.") || filePath.includes("/test/") || filePath.includes("/tests/");
4040
4189
  }
4041
- function isEntryPoint(filePath) {
4042
- const basename6 = path2.basename(filePath);
4043
- return basename6 === "index.ts" || basename6 === "index.js" || basename6 === "main.ts" || basename6 === "main.js" || basename6 === "app.ts" || basename6 === "app.js" || basename6 === "server.ts" || basename6 === "server.js";
4044
- }
4045
4190
  function isConfigFile(filePath) {
4046
4191
  return filePath.includes(".config.") || filePath.includes("config/") || filePath.includes("vite.config") || filePath.includes("rollup.config") || filePath.includes("webpack.config");
4047
4192
  }
@@ -4252,9 +4397,15 @@ function analyzeDeadCode(graph, projectRoot, options = {}) {
4252
4397
  includeTests: options.includeTests || false,
4253
4398
  verbose: options.verbose || false,
4254
4399
  stats: options.stats || false,
4255
- json: options.json || false
4400
+ json: options.json || false,
4401
+ debug: options.debug || false
4256
4402
  };
4257
- const rawDeadSymbols = findDeadSymbols(graph, projectRoot, opts.includeTests);
4403
+ const { symbols: rawDeadSymbols } = findDeadSymbols(
4404
+ graph,
4405
+ projectRoot,
4406
+ opts.includeTests,
4407
+ opts.debug
4408
+ );
4258
4409
  const classifiedSymbols = classifyDeadSymbols(rawDeadSymbols, graph);
4259
4410
  const filteredSymbols = filterByConfidence(classifiedSymbols, opts.confidence);
4260
4411
  const totalSymbols = graph.order;
@@ -4284,7 +4435,7 @@ function filterByConfidence(symbols, minConfidence) {
4284
4435
  }
4285
4436
 
4286
4437
  // src/docs/generator.ts
4287
- import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync11 } from "fs";
4438
+ import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync12 } from "fs";
4288
4439
  import { join as join14 } from "path";
4289
4440
 
4290
4441
  // src/docs/architecture.ts
@@ -7621,7 +7772,7 @@ function getTopLevelDir2(filePath) {
7621
7772
  }
7622
7773
 
7623
7774
  // src/docs/status.ts
7624
- import { readFileSync as readFileSync7, existsSync as existsSync9 } from "fs";
7775
+ import { readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
7625
7776
  import { join as join12 } from "path";
7626
7777
  function generateStatus(graph, projectRoot, version) {
7627
7778
  let output = "";
@@ -7656,11 +7807,11 @@ function getFileCount11(graph) {
7656
7807
  function extractComments(projectRoot, filePath) {
7657
7808
  const comments = [];
7658
7809
  const fullPath = join12(projectRoot, filePath);
7659
- if (!existsSync9(fullPath)) {
7810
+ if (!existsSync10(fullPath)) {
7660
7811
  return comments;
7661
7812
  }
7662
7813
  try {
7663
- const content = readFileSync7(fullPath, "utf-8");
7814
+ const content = readFileSync8(fullPath, "utf-8");
7664
7815
  const lines = content.split("\n");
7665
7816
  const patterns = [
7666
7817
  { type: "TODO", regex: /(?:\/\/|#|\/\*)\s*TODO:?\s*(.+)/i },
@@ -8222,15 +8373,15 @@ function generateConfidenceSection(title, description, symbols, projectRoot) {
8222
8373
  }
8223
8374
 
8224
8375
  // src/docs/metadata.ts
8225
- import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync2 } from "fs";
8376
+ import { existsSync as existsSync11, readFileSync as readFileSync9, writeFileSync as writeFileSync2 } from "fs";
8226
8377
  import { join as join13 } from "path";
8227
8378
  function loadMetadata(outputDir) {
8228
8379
  const metadataPath = join13(outputDir, "metadata.json");
8229
- if (!existsSync10(metadataPath)) {
8380
+ if (!existsSync11(metadataPath)) {
8230
8381
  return null;
8231
8382
  }
8232
8383
  try {
8233
- const content = readFileSync8(metadataPath, "utf-8");
8384
+ const content = readFileSync9(metadataPath, "utf-8");
8234
8385
  return JSON.parse(content);
8235
8386
  } catch (err) {
8236
8387
  console.error("Failed to load metadata:", err);
@@ -8281,7 +8432,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8281
8432
  const generated = [];
8282
8433
  const errors = [];
8283
8434
  try {
8284
- if (!existsSync11(options.outputDir)) {
8435
+ if (!existsSync12(options.outputDir)) {
8285
8436
  mkdirSync2(options.outputDir, { recursive: true });
8286
8437
  if (options.verbose) {
8287
8438
  console.log(`Created output directory: ${options.outputDir}`);
@@ -8501,11 +8652,11 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
8501
8652
 
8502
8653
  // src/mcp/tools.ts
8503
8654
  import { dirname as dirname16, join as join17 } from "path";
8504
- import { existsSync as existsSync14, readFileSync as readFileSync10 } from "fs";
8655
+ import { existsSync as existsSync15, readFileSync as readFileSync11 } from "fs";
8505
8656
 
8506
8657
  // src/mcp/connect.ts
8507
8658
  import simpleGit from "simple-git";
8508
- import { existsSync as existsSync12 } from "fs";
8659
+ import { existsSync as existsSync13 } from "fs";
8509
8660
  import { join as join15, basename as basename5, resolve as resolve2 } from "path";
8510
8661
  import { tmpdir, homedir } from "os";
8511
8662
  function validateProjectPath(source) {
@@ -8550,7 +8701,7 @@ async function connectToRepo(source, subdirectory, state) {
8550
8701
  const cloneDir = join15(reposDir, projectName);
8551
8702
  console.error(`Connecting to GitHub repo: ${source}`);
8552
8703
  const git = simpleGit();
8553
- if (existsSync12(cloneDir)) {
8704
+ if (existsSync13(cloneDir)) {
8554
8705
  console.error(`Repo already cloned at ${cloneDir}, pulling latest changes...`);
8555
8706
  try {
8556
8707
  await git.cwd(cloneDir).pull();
@@ -8577,7 +8728,7 @@ async function connectToRepo(source, subdirectory, state) {
8577
8728
  message: validation2.error
8578
8729
  };
8579
8730
  }
8580
- if (!existsSync12(source)) {
8731
+ if (!existsSync13(source)) {
8581
8732
  return {
8582
8733
  error: "Directory not found",
8583
8734
  message: `Directory does not exist: ${source}`
@@ -8593,7 +8744,7 @@ async function connectToRepo(source, subdirectory, state) {
8593
8744
  message: validation.error
8594
8745
  };
8595
8746
  }
8596
- if (!existsSync12(projectRoot)) {
8747
+ if (!existsSync13(projectRoot)) {
8597
8748
  return {
8598
8749
  error: "Project root not found",
8599
8750
  message: `Directory does not exist: ${projectRoot}`
@@ -8874,10 +9025,10 @@ function getWeekNumber(date) {
8874
9025
  }
8875
9026
 
8876
9027
  // src/temporal/snapshots.ts
8877
- import { writeFileSync as writeFileSync4, readFileSync as readFileSync9, mkdirSync as mkdirSync3, existsSync as existsSync13, readdirSync as readdirSync5 } from "fs";
9028
+ import { writeFileSync as writeFileSync4, readFileSync as readFileSync10, mkdirSync as mkdirSync3, existsSync as existsSync14, readdirSync as readdirSync5 } from "fs";
8878
9029
  import { join as join16 } from "path";
8879
9030
  function saveSnapshot(snapshot, outputDir) {
8880
- if (!existsSync13(outputDir)) {
9031
+ if (!existsSync14(outputDir)) {
8881
9032
  mkdirSync3(outputDir, { recursive: true });
8882
9033
  }
8883
9034
  const filename = `${snapshot.commitHash.substring(0, 8)}.json`;
@@ -8887,11 +9038,11 @@ function saveSnapshot(snapshot, outputDir) {
8887
9038
  function loadSnapshot(commitHash, outputDir) {
8888
9039
  const shortHash = commitHash.substring(0, 8);
8889
9040
  const filepath = join16(outputDir, `${shortHash}.json`);
8890
- if (!existsSync13(filepath)) {
9041
+ if (!existsSync14(filepath)) {
8891
9042
  return null;
8892
9043
  }
8893
9044
  try {
8894
- const content = readFileSync9(filepath, "utf-8");
9045
+ const content = readFileSync10(filepath, "utf-8");
8895
9046
  return JSON.parse(content);
8896
9047
  } catch {
8897
9048
  return null;
@@ -9666,7 +9817,7 @@ The server will keep running until you end the MCP session or press Ctrl+C.`;
9666
9817
  }
9667
9818
  async function handleGetProjectDocs(docType, state) {
9668
9819
  const docsDir = join17(state.projectRoot, ".depwire");
9669
- if (!existsSync14(docsDir)) {
9820
+ if (!existsSync15(docsDir)) {
9670
9821
  const errorMessage = `Project documentation has not been generated yet.
9671
9822
 
9672
9823
  Run \`depwire docs ${state.projectRoot}\` to generate codebase documentation.
@@ -9697,11 +9848,11 @@ Available document types:
9697
9848
  continue;
9698
9849
  }
9699
9850
  const filePath = join17(docsDir, metadata.documents[doc].file);
9700
- if (!existsSync14(filePath)) {
9851
+ if (!existsSync15(filePath)) {
9701
9852
  missing.push(doc);
9702
9853
  continue;
9703
9854
  }
9704
- const content = readFileSync10(filePath, "utf-8");
9855
+ const content = readFileSync11(filePath, "utf-8");
9705
9856
  if (docsToReturn.length > 1) {
9706
9857
  output += `
9707
9858
 
@@ -9733,9 +9884,9 @@ async function handleUpdateProjectDocs(docType, state) {
9733
9884
  const parseTime = (Date.now() - startTime) / 1e3;
9734
9885
  state.graph = graph;
9735
9886
  const packageJsonPath = join17(__dirname, "../../package.json");
9736
- const packageJson = JSON.parse(readFileSync10(packageJsonPath, "utf-8"));
9887
+ const packageJson = JSON.parse(readFileSync11(packageJsonPath, "utf-8"));
9737
9888
  const docsToGenerate = docType === "all" ? ["architecture", "conventions", "dependencies", "onboarding"] : [docType];
9738
- const docsExist = existsSync14(docsDir);
9889
+ const docsExist = existsSync15(docsDir);
9739
9890
  const result = await generateDocs(graph, state.projectRoot, packageJson.version, parseTime, {
9740
9891
  outputDir: docsDir,
9741
9892
  format: "markdown",
package/dist/index.js CHANGED
@@ -27,7 +27,7 @@ import {
27
27
  stashChanges,
28
28
  updateFileInGraph,
29
29
  watchProject
30
- } from "./chunk-UPAZDLUB.js";
30
+ } from "./chunk-3DXWSPZL.js";
31
31
 
32
32
  // src/index.ts
33
33
  import { Command } from "commander";
@@ -788,7 +788,7 @@ program.command("health").description("Analyze dependency architecture health (0
788
788
  process.exit(1);
789
789
  }
790
790
  });
791
- 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").action(async (directory, options) => {
791
+ 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) => {
792
792
  try {
793
793
  const projectRoot = directory ? resolve(directory) : findProjectRoot();
794
794
  const startTime = Date.now();
@@ -800,7 +800,8 @@ program.command("dead-code").description("Identify dead code - symbols defined b
800
800
  includeTests: options.includeTests || false,
801
801
  verbose: options.verbose || false,
802
802
  stats: options.stats || false,
803
- json: options.json || false
803
+ json: options.json || false,
804
+ debug: options.debug || false
804
805
  });
805
806
  if (options.json) {
806
807
  console.log(JSON.stringify(report, null, 2));
@@ -6,7 +6,7 @@ import {
6
6
  startMcpServer,
7
7
  updateFileInGraph,
8
8
  watchProject
9
- } from "./chunk-UPAZDLUB.js";
9
+ } from "./chunk-3DXWSPZL.js";
10
10
 
11
11
  // src/mcpb-entry.ts
12
12
  import { resolve } from "path";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "depwire-cli",
3
- "version": "0.9.1",
3
+ "version": "0.9.2",
4
4
  "description": "Code cross-reference visualization and AI context engine for TypeScript, JavaScript, Python, Go, Rust, and C. Zero native dependencies — works on Windows, macOS, and Linux.",
5
5
  "type": "module",
6
6
  "bin": {