depwire-cli 0.8.0 โ†’ 0.9.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 CHANGED
@@ -39,7 +39,7 @@ Depwire analyzes codebases to build a cross-reference graph showing how every fi
39
39
  - ๐Ÿ” **Impact analysis** โ€” "What breaks if I rename this function?" answered precisely
40
40
  - ๐Ÿงน **Dead code detection** โ€” Find symbols that are defined but never referenced, categorized by confidence level
41
41
  - ๐Ÿ‘€ **Live updates** โ€” Graph stays current as you edit code
42
- - ๐ŸŒ **Multi-language** โ€” TypeScript, JavaScript, Python, and Go
42
+ - ๐ŸŒ **Multi-language** โ€” TypeScript, JavaScript, Python, Go, and Rust
43
43
 
44
44
  ## Why Depwire?
45
45
 
@@ -177,6 +177,7 @@ Settings โ†’ Features โ†’ Experimental โ†’ Enable MCP โ†’ Add Server:
177
177
  | JavaScript | `.js`, `.jsx`, `.mjs`, `.cjs` | ES modules, CommonJS require(), JSX components |
178
178
  | Python | `.py` | Imports, classes, decorators, inheritance |
179
179
  | Go | `.go` | go.mod resolution, structs, interfaces, methods |
180
+ | Rust | `.rs` | Functions, structs, enums, traits, impl blocks, use declarations |
180
181
 
181
182
  ## Visualization
182
183
 
@@ -612,10 +613,26 @@ See [SECURITY.md](SECURITY.md) for full details.
612
613
 
613
614
  ## Roadmap
614
615
 
615
- - [ ] PR Impact Visualization (GitHub Action)
616
- - [ ] Temporal Graph โ€” watch your architecture evolve over git history
616
+ ### โœ… Shipped
617
+ - [x] Arc diagram visualization
618
+ - [x] MCP server (15 tools)
619
+ - [x] Multi-language support (TypeScript, JavaScript, Python, Go, Rust)
620
+ - [x] File watching + live refresh
621
+ - [x] Auto-generated documentation (13 documents)
622
+ - [x] Dependency health score (0-100)
623
+ - [x] Dead code detection with confidence scoring
624
+ - [x] Temporal graph โ€” watch your architecture evolve over git history
625
+ - [x] PR Impact GitHub Action (depwire-action v1.0.0)
626
+ - [x] Auto-detect project root (no path needed)
627
+ - [x] WASM migration (Windows support)
628
+
629
+ ### ๐Ÿ”œ Coming Next
630
+ - [ ] New language support (C โ€” community requested)
631
+ - [ ] "What If" simulation โ€” simulate refactors before touching code
617
632
  - [ ] Cross-language edge detection (API routes โ†” frontend calls)
618
- - [ ] Dependency health scoring
633
+ - [ ] Cloud dashboard (first paid feature)
634
+ - [ ] AI-suggested refactors
635
+ - [ ] Natural language architecture queries
619
636
  - [ ] VSCode extension
620
637
 
621
638
  ## Contributing
@@ -29,7 +29,8 @@ function scanDirectory(rootDir, baseDir = rootDir) {
29
29
  const isJavaScript = entry.endsWith(".js") || entry.endsWith(".jsx") || entry.endsWith(".mjs") || entry.endsWith(".cjs");
30
30
  const isPython = entry.endsWith(".py");
31
31
  const isGo = entry.endsWith(".go") && !entry.endsWith("_test.go");
32
- if (isTypeScript || isJavaScript || isPython || isGo) {
32
+ const isRust = entry.endsWith(".rs");
33
+ if (isTypeScript || isJavaScript || isPython || isGo || isRust) {
33
34
  files.push(relative(rootDir, fullPath));
34
35
  }
35
36
  }
@@ -54,6 +55,8 @@ function findProjectRoot(startDir = process.cwd()) {
54
55
  // TypeScript
55
56
  "go.mod",
56
57
  // Go
58
+ "Cargo.toml",
59
+ // Rust
57
60
  "pyproject.toml",
58
61
  // Python (modern)
59
62
  "setup.py",
@@ -80,8 +83,8 @@ function findProjectRoot(startDir = process.cwd()) {
80
83
  }
81
84
 
82
85
  // src/parser/index.ts
83
- import { readFileSync as readFileSync3, statSync as statSync2 } from "fs";
84
- import { join as join6 } from "path";
86
+ import { readFileSync as readFileSync4, statSync as statSync2 } from "fs";
87
+ import { join as join7 } from "path";
85
88
 
86
89
  // src/parser/detect.ts
87
90
  import { extname as extname3 } from "path";
@@ -109,7 +112,8 @@ async function initParser() {
109
112
  "tsx": "tree-sitter-tsx.wasm",
110
113
  "javascript": "tree-sitter-javascript.wasm",
111
114
  "python": "tree-sitter-python.wasm",
112
- "go": "tree-sitter-go.wasm"
115
+ "go": "tree-sitter-go.wasm",
116
+ "rust": "tree-sitter-rust.wasm"
113
117
  };
114
118
  for (const [name, file] of Object.entries(grammarFiles)) {
115
119
  const wasmPath = path.join(grammarsDir, file);
@@ -1972,12 +1976,429 @@ var goParser = {
1972
1976
  parseFile: parseGoFile
1973
1977
  };
1974
1978
 
1979
+ // src/parser/rust.ts
1980
+ import { existsSync as existsSync6 } from "fs";
1981
+ import { join as join6, dirname as dirname5, relative as relative3 } from "path";
1982
+ function parseRustFile(filePath, sourceCode, projectRoot) {
1983
+ const parser = getParser("rust");
1984
+ const tree = parser.parse(sourceCode, null, { bufferSize: 1024 * 1024 });
1985
+ const context = {
1986
+ filePath,
1987
+ projectRoot,
1988
+ sourceCode,
1989
+ symbols: [],
1990
+ edges: [],
1991
+ currentScope: [],
1992
+ currentModule: []
1993
+ };
1994
+ walkNode5(tree.rootNode, context);
1995
+ return {
1996
+ filePath,
1997
+ symbols: context.symbols,
1998
+ edges: context.edges
1999
+ };
2000
+ }
2001
+ function walkNode5(node, context) {
2002
+ processNode5(node, context);
2003
+ for (let i = 0; i < node.childCount; i++) {
2004
+ const child = node.child(i);
2005
+ if (child) {
2006
+ walkNode5(child, context);
2007
+ }
2008
+ }
2009
+ }
2010
+ function processNode5(node, context) {
2011
+ const type = node.type;
2012
+ switch (type) {
2013
+ case "function_item":
2014
+ processFunctionItem(node, context);
2015
+ break;
2016
+ case "struct_item":
2017
+ processStructItem(node, context);
2018
+ break;
2019
+ case "enum_item":
2020
+ processEnumItem(node, context);
2021
+ break;
2022
+ case "trait_item":
2023
+ processTraitItem(node, context);
2024
+ break;
2025
+ case "impl_item":
2026
+ processImplItem(node, context);
2027
+ break;
2028
+ case "const_item":
2029
+ processConstItem(node, context);
2030
+ break;
2031
+ case "type_item":
2032
+ processTypeItem(node, context);
2033
+ break;
2034
+ case "use_declaration":
2035
+ processUseDeclaration(node, context);
2036
+ break;
2037
+ case "mod_item":
2038
+ processModItem(node, context);
2039
+ break;
2040
+ case "call_expression":
2041
+ processCallExpression5(node, context);
2042
+ break;
2043
+ }
2044
+ }
2045
+ function processFunctionItem(node, context) {
2046
+ const nameNode = node.childForFieldName("name");
2047
+ if (!nameNode) return;
2048
+ const name = nodeText4(nameNode, context);
2049
+ const exported = hasVisibility(node, "pub");
2050
+ const symbolId = `${context.filePath}::${name}`;
2051
+ context.symbols.push({
2052
+ id: symbolId,
2053
+ name,
2054
+ kind: "function",
2055
+ filePath: context.filePath,
2056
+ startLine: node.startPosition.row + 1,
2057
+ endLine: node.endPosition.row + 1,
2058
+ exported
2059
+ });
2060
+ context.currentScope.push(name);
2061
+ const body = node.childForFieldName("body");
2062
+ if (body) {
2063
+ walkNode5(body, context);
2064
+ }
2065
+ context.currentScope.pop();
2066
+ }
2067
+ function processStructItem(node, context) {
2068
+ const nameNode = node.childForFieldName("name");
2069
+ if (!nameNode) return;
2070
+ const name = nodeText4(nameNode, context);
2071
+ const exported = hasVisibility(node, "pub");
2072
+ const symbolId = `${context.filePath}::${name}`;
2073
+ context.symbols.push({
2074
+ id: symbolId,
2075
+ name,
2076
+ kind: "class",
2077
+ // Consistent with other parsers
2078
+ filePath: context.filePath,
2079
+ startLine: node.startPosition.row + 1,
2080
+ endLine: node.endPosition.row + 1,
2081
+ exported
2082
+ });
2083
+ }
2084
+ function processEnumItem(node, context) {
2085
+ const nameNode = node.childForFieldName("name");
2086
+ if (!nameNode) return;
2087
+ const name = nodeText4(nameNode, context);
2088
+ const exported = hasVisibility(node, "pub");
2089
+ const symbolId = `${context.filePath}::${name}`;
2090
+ context.symbols.push({
2091
+ id: symbolId,
2092
+ name,
2093
+ kind: "enum",
2094
+ filePath: context.filePath,
2095
+ startLine: node.startPosition.row + 1,
2096
+ endLine: node.endPosition.row + 1,
2097
+ exported
2098
+ });
2099
+ }
2100
+ function processTraitItem(node, context) {
2101
+ const nameNode = node.childForFieldName("name");
2102
+ if (!nameNode) return;
2103
+ const name = nodeText4(nameNode, context);
2104
+ const exported = hasVisibility(node, "pub");
2105
+ const symbolId = `${context.filePath}::${name}`;
2106
+ context.symbols.push({
2107
+ id: symbolId,
2108
+ name,
2109
+ kind: "interface",
2110
+ filePath: context.filePath,
2111
+ startLine: node.startPosition.row + 1,
2112
+ endLine: node.endPosition.row + 1,
2113
+ exported
2114
+ });
2115
+ }
2116
+ function processImplItem(node, context) {
2117
+ const typeNode = node.childForFieldName("type");
2118
+ if (!typeNode) return;
2119
+ const typeName = extractTypeName2(typeNode, context);
2120
+ if (!typeName) return;
2121
+ for (let i = 0; i < node.childCount; i++) {
2122
+ const child = node.child(i);
2123
+ if (child && child.type === "function_item") {
2124
+ const nameNode = child.childForFieldName("name");
2125
+ if (!nameNode) continue;
2126
+ const name = nodeText4(nameNode, context);
2127
+ const exported = hasVisibility(child, "pub");
2128
+ const symbolId = `${context.filePath}::${typeName}.${name}`;
2129
+ context.symbols.push({
2130
+ id: symbolId,
2131
+ name,
2132
+ kind: "method",
2133
+ filePath: context.filePath,
2134
+ startLine: child.startPosition.row + 1,
2135
+ endLine: child.endPosition.row + 1,
2136
+ exported,
2137
+ scope: typeName
2138
+ });
2139
+ context.currentScope.push(`${typeName}.${name}`);
2140
+ const body = child.childForFieldName("body");
2141
+ if (body) {
2142
+ walkNode5(body, context);
2143
+ }
2144
+ context.currentScope.pop();
2145
+ }
2146
+ }
2147
+ }
2148
+ function processConstItem(node, context) {
2149
+ const nameNode = node.childForFieldName("name");
2150
+ if (!nameNode) return;
2151
+ const name = nodeText4(nameNode, context);
2152
+ const exported = hasVisibility(node, "pub");
2153
+ const symbolId = `${context.filePath}::${name}`;
2154
+ context.symbols.push({
2155
+ id: symbolId,
2156
+ name,
2157
+ kind: "constant",
2158
+ filePath: context.filePath,
2159
+ startLine: node.startPosition.row + 1,
2160
+ endLine: node.endPosition.row + 1,
2161
+ exported
2162
+ });
2163
+ }
2164
+ function processTypeItem(node, context) {
2165
+ const nameNode = node.childForFieldName("name");
2166
+ if (!nameNode) return;
2167
+ const name = nodeText4(nameNode, context);
2168
+ const exported = hasVisibility(node, "pub");
2169
+ const symbolId = `${context.filePath}::${name}`;
2170
+ context.symbols.push({
2171
+ id: symbolId,
2172
+ name,
2173
+ kind: "type_alias",
2174
+ filePath: context.filePath,
2175
+ startLine: node.startPosition.row + 1,
2176
+ endLine: node.endPosition.row + 1,
2177
+ exported
2178
+ });
2179
+ }
2180
+ function processUseDeclaration(node, context) {
2181
+ let pathNode = findChildByType5(node, "scoped_identifier");
2182
+ if (!pathNode) {
2183
+ pathNode = findChildByType5(node, "identifier");
2184
+ }
2185
+ if (!pathNode) {
2186
+ pathNode = findChildByType5(node, "use_as_clause");
2187
+ if (pathNode) {
2188
+ pathNode = pathNode.childForFieldName("path");
2189
+ }
2190
+ }
2191
+ if (!pathNode) {
2192
+ return;
2193
+ }
2194
+ let pathText = nodeText4(pathNode, context);
2195
+ if (!pathText.startsWith("crate::") && !pathText.startsWith("super::") && !pathText.startsWith("self::")) {
2196
+ return;
2197
+ }
2198
+ const segments = pathText.split("::");
2199
+ if (segments.length > 1) {
2200
+ segments.pop();
2201
+ pathText = segments.join("::");
2202
+ }
2203
+ const resolvedFiles = resolveRustImport(pathText, context);
2204
+ if (resolvedFiles.length === 0) return;
2205
+ const sourceId = `${context.filePath}::__file__`;
2206
+ for (const targetFile of resolvedFiles) {
2207
+ const targetId = `${targetFile}::__file__`;
2208
+ context.edges.push({
2209
+ source: sourceId,
2210
+ target: targetId,
2211
+ kind: "imports",
2212
+ filePath: context.filePath,
2213
+ line: node.startPosition.row + 1
2214
+ });
2215
+ }
2216
+ }
2217
+ function processModItem(node, context) {
2218
+ const nameNode = node.childForFieldName("name");
2219
+ if (!nameNode) return;
2220
+ const name = nodeText4(nameNode, context);
2221
+ const body = node.childForFieldName("body");
2222
+ if (!body) {
2223
+ const resolvedFiles = resolveModuleFile(name, context);
2224
+ if (resolvedFiles.length > 0) {
2225
+ const sourceId = `${context.filePath}::__file__`;
2226
+ for (const targetFile of resolvedFiles) {
2227
+ const targetId = `${targetFile}::__file__`;
2228
+ context.edges.push({
2229
+ source: sourceId,
2230
+ target: targetId,
2231
+ kind: "imports",
2232
+ filePath: context.filePath,
2233
+ line: node.startPosition.row + 1
2234
+ });
2235
+ }
2236
+ }
2237
+ }
2238
+ const exported = hasVisibility(node, "pub");
2239
+ const symbolId = `${context.filePath}::${name}`;
2240
+ context.symbols.push({
2241
+ id: symbolId,
2242
+ name,
2243
+ kind: "module",
2244
+ filePath: context.filePath,
2245
+ startLine: node.startPosition.row + 1,
2246
+ endLine: node.endPosition.row + 1,
2247
+ exported
2248
+ });
2249
+ }
2250
+ function processCallExpression5(node, context) {
2251
+ const functionNode = node.childForFieldName("function");
2252
+ if (!functionNode) return;
2253
+ const calleeName = extractCalleeNameFromNode(functionNode, context);
2254
+ if (!calleeName) return;
2255
+ const builtins = ["println!", "print!", "eprintln!", "eprint!", "format!", "panic!", "assert!", "assert_eq!", "assert_ne!", "vec!"];
2256
+ if (builtins.includes(calleeName)) return;
2257
+ const callerId = getCurrentSymbolId5(context);
2258
+ if (!callerId) return;
2259
+ const calleeId = resolveSymbol4(calleeName, context);
2260
+ if (!calleeId) return;
2261
+ context.edges.push({
2262
+ source: callerId,
2263
+ target: calleeId,
2264
+ kind: "calls",
2265
+ filePath: context.filePath,
2266
+ line: node.startPosition.row + 1
2267
+ });
2268
+ }
2269
+ function resolveRustImport(importPath, context) {
2270
+ if (importPath.startsWith("crate::")) {
2271
+ const relativePath = importPath.replace("crate::", "").replace(/::/g, "/");
2272
+ const possibleFiles = [
2273
+ join6(context.projectRoot, "src", `${relativePath}.rs`),
2274
+ join6(context.projectRoot, "src", relativePath, "mod.rs")
2275
+ ];
2276
+ return possibleFiles.filter((f) => existsSync6(f)).map((f) => relative3(context.projectRoot, f));
2277
+ }
2278
+ if (importPath.startsWith("super::")) {
2279
+ const currentFileAbs = join6(context.projectRoot, context.filePath);
2280
+ const currentDir = dirname5(currentFileAbs);
2281
+ const parentDir = dirname5(currentDir);
2282
+ const relativePath = importPath.replace("super::", "").replace(/::/g, "/");
2283
+ const possibleFiles = [
2284
+ join6(parentDir, `${relativePath}.rs`),
2285
+ join6(parentDir, relativePath, "mod.rs")
2286
+ ];
2287
+ return possibleFiles.filter((f) => existsSync6(f)).map((f) => relative3(context.projectRoot, f));
2288
+ }
2289
+ if (importPath.startsWith("self::")) {
2290
+ const currentFileAbs = join6(context.projectRoot, context.filePath);
2291
+ const currentDir = dirname5(currentFileAbs);
2292
+ const relativePath = importPath.replace("self::", "").replace(/::/g, "/");
2293
+ const possibleFiles = [
2294
+ join6(currentDir, `${relativePath}.rs`),
2295
+ join6(currentDir, relativePath, "mod.rs")
2296
+ ];
2297
+ return possibleFiles.filter((f) => existsSync6(f)).map((f) => relative3(context.projectRoot, f));
2298
+ }
2299
+ return [];
2300
+ }
2301
+ function resolveModuleFile(moduleName, context) {
2302
+ const currentFileAbs = join6(context.projectRoot, context.filePath);
2303
+ const currentDir = dirname5(currentFileAbs);
2304
+ const possibleFiles = [
2305
+ join6(currentDir, `${moduleName}.rs`),
2306
+ join6(currentDir, moduleName, "mod.rs")
2307
+ ];
2308
+ return possibleFiles.filter((f) => existsSync6(f)).map((f) => relative3(context.projectRoot, f));
2309
+ }
2310
+ function hasVisibility(node, visibility) {
2311
+ for (let i = 0; i < node.childCount; i++) {
2312
+ const child = node.child(i);
2313
+ if (child && child.type === "visibility_modifier") {
2314
+ const text = nodeText4(child, { sourceCode: node.text });
2315
+ return text === visibility;
2316
+ }
2317
+ }
2318
+ return false;
2319
+ }
2320
+ function extractTypeName2(typeNode, context) {
2321
+ if (typeNode.type === "type_identifier") {
2322
+ return nodeText4(typeNode, context);
2323
+ }
2324
+ if (typeNode.type === "generic_type") {
2325
+ const typeId = findChildByType5(typeNode, "type_identifier");
2326
+ if (typeId) {
2327
+ return nodeText4(typeId, context);
2328
+ }
2329
+ }
2330
+ for (let i = 0; i < typeNode.childCount; i++) {
2331
+ const child = typeNode.child(i);
2332
+ if (child && child.type === "type_identifier") {
2333
+ return nodeText4(child, context);
2334
+ }
2335
+ }
2336
+ return null;
2337
+ }
2338
+ function extractCalleeNameFromNode(functionNode, context) {
2339
+ if (functionNode.type === "identifier") {
2340
+ return nodeText4(functionNode, context);
2341
+ }
2342
+ if (functionNode.type === "field_expression") {
2343
+ const field = functionNode.childForFieldName("field");
2344
+ if (field) {
2345
+ return nodeText4(field, context);
2346
+ }
2347
+ }
2348
+ if (functionNode.type === "scoped_identifier") {
2349
+ const name = functionNode.childForFieldName("name");
2350
+ if (name) {
2351
+ return nodeText4(name, context);
2352
+ }
2353
+ }
2354
+ return null;
2355
+ }
2356
+ function resolveSymbol4(name, context) {
2357
+ const currentFileId = context.filePath;
2358
+ const symbol = context.symbols.find((s) => s.name === name && s.filePath === currentFileId);
2359
+ if (symbol) {
2360
+ return symbol.id;
2361
+ }
2362
+ if (context.currentScope.length > 0) {
2363
+ for (let i = context.currentScope.length - 1; i >= 0; i--) {
2364
+ const scopedId = `${currentFileId}::${context.currentScope[i]}.${name}`;
2365
+ const scopedSymbol = context.symbols.find((s) => s.id === scopedId);
2366
+ if (scopedSymbol) {
2367
+ return scopedId;
2368
+ }
2369
+ }
2370
+ }
2371
+ return null;
2372
+ }
2373
+ function findChildByType5(node, type) {
2374
+ for (let i = 0; i < node.childCount; i++) {
2375
+ const child = node.child(i);
2376
+ if (child && child.type === type) {
2377
+ return child;
2378
+ }
2379
+ }
2380
+ return null;
2381
+ }
2382
+ function nodeText4(node, context) {
2383
+ return context.sourceCode.slice(node.startIndex, node.endIndex);
2384
+ }
2385
+ function getCurrentSymbolId5(context) {
2386
+ if (context.currentScope.length === 0) return null;
2387
+ return `${context.filePath}::${context.currentScope.join(".")}`;
2388
+ }
2389
+ var rustParser = {
2390
+ language: "rust",
2391
+ extensions: [".rs"],
2392
+ parseFile: parseRustFile
2393
+ };
2394
+
1975
2395
  // src/parser/detect.ts
1976
2396
  var parsers = [
1977
2397
  typescriptParser,
1978
2398
  pythonParser,
1979
2399
  javascriptParser,
1980
- goParser
2400
+ goParser,
2401
+ rustParser
1981
2402
  ];
1982
2403
  function getParserForFile(filePath) {
1983
2404
  const ext = extname3(filePath).toLowerCase();
@@ -2007,7 +2428,7 @@ async function parseProject(projectRoot, options) {
2007
2428
  let errorFiles = 0;
2008
2429
  for (const file of files) {
2009
2430
  try {
2010
- const fullPath = join6(projectRoot, file);
2431
+ const fullPath = join7(projectRoot, file);
2011
2432
  if (options?.exclude) {
2012
2433
  const shouldExclude2 = options.exclude.some(
2013
2434
  (pattern) => minimatch(file, pattern, { matchBase: true })
@@ -2033,7 +2454,7 @@ async function parseProject(projectRoot, options) {
2033
2454
  skippedFiles++;
2034
2455
  continue;
2035
2456
  }
2036
- const sourceCode = readFileSync3(fullPath, "utf-8");
2457
+ const sourceCode = readFileSync4(fullPath, "utf-8");
2037
2458
  const parsed = parser.parseFile(file, sourceCode, projectRoot);
2038
2459
  parsedFiles.push(parsed);
2039
2460
  } catch (err) {
@@ -2089,6 +2510,18 @@ function buildGraph(parsedFiles) {
2089
2510
  exported: false
2090
2511
  });
2091
2512
  }
2513
+ if (edge.target.endsWith("::__file__") && !fileNodes.has(edge.target)) {
2514
+ fileNodes.add(edge.target);
2515
+ const filePath = edge.target.replace("::__file__", "");
2516
+ graph.addNode(edge.target, {
2517
+ name: "__file__",
2518
+ kind: "import",
2519
+ filePath,
2520
+ startLine: 1,
2521
+ endLine: 1,
2522
+ exported: false
2523
+ });
2524
+ }
2092
2525
  }
2093
2526
  }
2094
2527
  for (const file of parsedFiles) {
@@ -2414,7 +2847,7 @@ function watchProject(projectRoot, callbacks) {
2414
2847
  const watcher = chokidar.watch(projectRoot, watcherOptions);
2415
2848
  console.error("[Watcher] Attaching event listeners...");
2416
2849
  watcher.on("change", (absolutePath) => {
2417
- const validExtensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".go"];
2850
+ const validExtensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".go", ".rs"];
2418
2851
  if (!validExtensions.some((ext) => absolutePath.endsWith(ext))) return;
2419
2852
  if (absolutePath.endsWith("_test.go")) return;
2420
2853
  const relativePath = absolutePath.replace(projectRoot + "/", "");
@@ -2422,7 +2855,7 @@ function watchProject(projectRoot, callbacks) {
2422
2855
  callbacks.onFileChanged(relativePath);
2423
2856
  });
2424
2857
  watcher.on("add", (absolutePath) => {
2425
- const validExtensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".go"];
2858
+ const validExtensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".go", ".rs"];
2426
2859
  if (!validExtensions.some((ext) => absolutePath.endsWith(ext))) return;
2427
2860
  if (absolutePath.endsWith("_test.go")) return;
2428
2861
  const relativePath = absolutePath.replace(projectRoot + "/", "");
@@ -2430,7 +2863,7 @@ function watchProject(projectRoot, callbacks) {
2430
2863
  callbacks.onFileAdded(relativePath);
2431
2864
  });
2432
2865
  watcher.on("unlink", (absolutePath) => {
2433
- const validExtensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".go"];
2866
+ const validExtensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".go", ".rs"];
2434
2867
  if (!validExtensions.some((ext) => absolutePath.endsWith(ext))) return;
2435
2868
  if (absolutePath.endsWith("_test.go")) return;
2436
2869
  const relativePath = absolutePath.replace(projectRoot + "/", "");
@@ -2460,10 +2893,10 @@ function watchProject(projectRoot, callbacks) {
2460
2893
  import express from "express";
2461
2894
  import open from "open";
2462
2895
  import { fileURLToPath as fileURLToPath2 } from "url";
2463
- import { dirname as dirname5, join as join7 } from "path";
2896
+ import { dirname as dirname6, join as join8 } from "path";
2464
2897
  import { WebSocketServer } from "ws";
2465
2898
  var __filename = fileURLToPath2(import.meta.url);
2466
- var __dirname2 = dirname5(__filename);
2899
+ var __dirname2 = dirname6(__filename);
2467
2900
  var activeServer = null;
2468
2901
  async function findAvailablePort(startPort, maxAttempts = 10) {
2469
2902
  const net = await import("net");
@@ -2501,7 +2934,7 @@ async function startVizServer(initialVizData, graph, projectRoot, port = 3333, s
2501
2934
  const availablePort = await findAvailablePort(port);
2502
2935
  const app = express();
2503
2936
  let vizData = initialVizData;
2504
- const publicDir = join7(__dirname2, "viz", "public");
2937
+ const publicDir = join8(__dirname2, "viz", "public");
2505
2938
  app.use(express.static(publicDir));
2506
2939
  app.get("/api/graph", (req, res) => {
2507
2940
  res.json(vizData);
@@ -2617,7 +3050,7 @@ function isProjectLoaded(state) {
2617
3050
  }
2618
3051
 
2619
3052
  // src/graph/updater.ts
2620
- import { join as join8 } from "path";
3053
+ import { join as join9 } from "path";
2621
3054
  function removeFileFromGraph(graph, filePath) {
2622
3055
  const nodesToRemove = [];
2623
3056
  graph.forEachNode((node, attrs) => {
@@ -2661,7 +3094,7 @@ function addFileToGraph(graph, parsedFile) {
2661
3094
  }
2662
3095
  async function updateFileInGraph(graph, projectRoot, relativeFilePath) {
2663
3096
  removeFileFromGraph(graph, relativeFilePath);
2664
- const absolutePath = join8(projectRoot, relativeFilePath);
3097
+ const absolutePath = join9(projectRoot, relativeFilePath);
2665
3098
  try {
2666
3099
  const parsedFile = parseTypeScriptFile(absolutePath, relativeFilePath);
2667
3100
  addFileToGraph(graph, parsedFile);
@@ -2671,7 +3104,7 @@ async function updateFileInGraph(graph, projectRoot, relativeFilePath) {
2671
3104
  }
2672
3105
 
2673
3106
  // src/health/metrics.ts
2674
- import { dirname as dirname6 } from "path";
3107
+ import { dirname as dirname7 } from "path";
2675
3108
  function scoreToGrade(score) {
2676
3109
  if (score >= 90) return "A";
2677
3110
  if (score >= 80) return "B";
@@ -2704,8 +3137,8 @@ function calculateCouplingScore(graph) {
2704
3137
  totalEdges++;
2705
3138
  fileConnections.set(sourceAttrs.filePath, (fileConnections.get(sourceAttrs.filePath) || 0) + 1);
2706
3139
  fileConnections.set(targetAttrs.filePath, (fileConnections.get(targetAttrs.filePath) || 0) + 1);
2707
- const sourceDir = dirname6(sourceAttrs.filePath).split("/")[0];
2708
- const targetDir = dirname6(targetAttrs.filePath).split("/")[0];
3140
+ const sourceDir = dirname7(sourceAttrs.filePath).split("/")[0];
3141
+ const targetDir = dirname7(targetAttrs.filePath).split("/")[0];
2709
3142
  if (sourceDir !== targetDir) {
2710
3143
  crossDirEdges++;
2711
3144
  }
@@ -2752,8 +3185,8 @@ function calculateCohesionScore(graph) {
2752
3185
  const sourceAttrs = graph.getNodeAttributes(source);
2753
3186
  const targetAttrs = graph.getNodeAttributes(target);
2754
3187
  if (sourceAttrs.filePath !== targetAttrs.filePath) {
2755
- const sourceDir = dirname6(sourceAttrs.filePath);
2756
- const targetDir = dirname6(targetAttrs.filePath);
3188
+ const sourceDir = dirname7(sourceAttrs.filePath);
3189
+ const targetDir = dirname7(targetAttrs.filePath);
2757
3190
  if (!dirEdges.has(sourceDir)) {
2758
3191
  dirEdges.set(sourceDir, { internal: 0, total: 0 });
2759
3192
  }
@@ -3035,8 +3468,8 @@ function calculateDepthScore(graph) {
3035
3468
  }
3036
3469
 
3037
3470
  // src/health/index.ts
3038
- import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync6, mkdirSync } from "fs";
3039
- import { join as join9, dirname as dirname7 } from "path";
3471
+ import { readFileSync as readFileSync5, writeFileSync, existsSync as existsSync7, mkdirSync } from "fs";
3472
+ import { join as join10, dirname as dirname8 } from "path";
3040
3473
  function calculateHealthScore(graph, projectRoot) {
3041
3474
  const coupling = calculateCouplingScore(graph);
3042
3475
  const cohesion = calculateCohesionScore(graph);
@@ -3135,7 +3568,7 @@ function getHealthTrend(projectRoot, currentScore) {
3135
3568
  }
3136
3569
  }
3137
3570
  function saveHealthHistory(projectRoot, report) {
3138
- const historyFile = join9(projectRoot, ".depwire", "health-history.json");
3571
+ const historyFile = join10(projectRoot, ".depwire", "health-history.json");
3139
3572
  const entry = {
3140
3573
  timestamp: report.timestamp,
3141
3574
  score: report.overall,
@@ -3147,9 +3580,9 @@ function saveHealthHistory(projectRoot, report) {
3147
3580
  }))
3148
3581
  };
3149
3582
  let history = [];
3150
- if (existsSync6(historyFile)) {
3583
+ if (existsSync7(historyFile)) {
3151
3584
  try {
3152
- const content = readFileSync4(historyFile, "utf-8");
3585
+ const content = readFileSync5(historyFile, "utf-8");
3153
3586
  history = JSON.parse(content);
3154
3587
  } catch {
3155
3588
  }
@@ -3158,16 +3591,16 @@ function saveHealthHistory(projectRoot, report) {
3158
3591
  if (history.length > 50) {
3159
3592
  history = history.slice(-50);
3160
3593
  }
3161
- mkdirSync(dirname7(historyFile), { recursive: true });
3594
+ mkdirSync(dirname8(historyFile), { recursive: true });
3162
3595
  writeFileSync(historyFile, JSON.stringify(history, null, 2), "utf-8");
3163
3596
  }
3164
3597
  function loadHealthHistory(projectRoot) {
3165
- const historyFile = join9(projectRoot, ".depwire", "health-history.json");
3166
- if (!existsSync6(historyFile)) {
3598
+ const historyFile = join10(projectRoot, ".depwire", "health-history.json");
3599
+ if (!existsSync7(historyFile)) {
3167
3600
  return [];
3168
3601
  }
3169
3602
  try {
3170
- const content = readFileSync4(historyFile, "utf-8");
3603
+ const content = readFileSync5(historyFile, "utf-8");
3171
3604
  return JSON.parse(content);
3172
3605
  } catch {
3173
3606
  return [];
@@ -3473,11 +3906,11 @@ function filterByConfidence(symbols, minConfidence) {
3473
3906
  }
3474
3907
 
3475
3908
  // src/docs/generator.ts
3476
- import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync9 } from "fs";
3477
- import { join as join12 } from "path";
3909
+ import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync10 } from "fs";
3910
+ import { join as join13 } from "path";
3478
3911
 
3479
3912
  // src/docs/architecture.ts
3480
- import { dirname as dirname8 } from "path";
3913
+ import { dirname as dirname9 } from "path";
3481
3914
 
3482
3915
  // src/docs/templates.ts
3483
3916
  function header(text, level = 1) {
@@ -3628,7 +4061,7 @@ function generateModuleStructure(graph) {
3628
4061
  function getDirectoryStats(graph) {
3629
4062
  const dirMap = /* @__PURE__ */ new Map();
3630
4063
  graph.forEachNode((node, attrs) => {
3631
- const dir = dirname8(attrs.filePath);
4064
+ const dir = dirname9(attrs.filePath);
3632
4065
  if (dir === ".") return;
3633
4066
  if (!dirMap.has(dir)) {
3634
4067
  dirMap.set(dir, {
@@ -3653,7 +4086,7 @@ function getDirectoryStats(graph) {
3653
4086
  });
3654
4087
  const filesPerDir = /* @__PURE__ */ new Map();
3655
4088
  graph.forEachNode((node, attrs) => {
3656
- const dir = dirname8(attrs.filePath);
4089
+ const dir = dirname9(attrs.filePath);
3657
4090
  if (!filesPerDir.has(dir)) {
3658
4091
  filesPerDir.set(dir, /* @__PURE__ */ new Set());
3659
4092
  }
@@ -3668,8 +4101,8 @@ function getDirectoryStats(graph) {
3668
4101
  graph.forEachEdge((edge, attrs, source, target) => {
3669
4102
  const sourceAttrs = graph.getNodeAttributes(source);
3670
4103
  const targetAttrs = graph.getNodeAttributes(target);
3671
- const sourceDir = dirname8(sourceAttrs.filePath);
3672
- const targetDir = dirname8(targetAttrs.filePath);
4104
+ const sourceDir = dirname9(sourceAttrs.filePath);
4105
+ const targetDir = dirname9(targetAttrs.filePath);
3673
4106
  if (sourceDir !== targetDir) {
3674
4107
  if (!dirEdges.has(sourceDir)) {
3675
4108
  dirEdges.set(sourceDir, { in: 0, out: 0 });
@@ -4650,7 +5083,7 @@ function detectCyclesDetailed(graph) {
4650
5083
  }
4651
5084
 
4652
5085
  // src/docs/onboarding.ts
4653
- import { dirname as dirname9 } from "path";
5086
+ import { dirname as dirname10 } from "path";
4654
5087
  function generateOnboarding(graph, projectRoot, version) {
4655
5088
  let output = "";
4656
5089
  const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -4709,7 +5142,7 @@ function generateQuickOrientation(graph) {
4709
5142
  const primaryLang = Object.entries(languages2).sort((a, b) => b[1] - a[1])[0];
4710
5143
  const dirs = /* @__PURE__ */ new Set();
4711
5144
  graph.forEachNode((node, attrs) => {
4712
- const dir = dirname9(attrs.filePath);
5145
+ const dir = dirname10(attrs.filePath);
4713
5146
  if (dir !== ".") {
4714
5147
  const topLevel = dir.split("/")[0];
4715
5148
  dirs.add(topLevel);
@@ -4813,7 +5246,7 @@ function generateModuleMap(graph) {
4813
5246
  function getDirectoryStats2(graph) {
4814
5247
  const dirMap = /* @__PURE__ */ new Map();
4815
5248
  graph.forEachNode((node, attrs) => {
4816
- const dir = dirname9(attrs.filePath);
5249
+ const dir = dirname10(attrs.filePath);
4817
5250
  if (dir === ".") return;
4818
5251
  if (!dirMap.has(dir)) {
4819
5252
  dirMap.set(dir, {
@@ -4828,7 +5261,7 @@ function getDirectoryStats2(graph) {
4828
5261
  });
4829
5262
  const filesPerDir = /* @__PURE__ */ new Map();
4830
5263
  graph.forEachNode((node, attrs) => {
4831
- const dir = dirname9(attrs.filePath);
5264
+ const dir = dirname10(attrs.filePath);
4832
5265
  if (!filesPerDir.has(dir)) {
4833
5266
  filesPerDir.set(dir, /* @__PURE__ */ new Set());
4834
5267
  }
@@ -4843,8 +5276,8 @@ function getDirectoryStats2(graph) {
4843
5276
  graph.forEachEdge((edge, attrs, source, target) => {
4844
5277
  const sourceAttrs = graph.getNodeAttributes(source);
4845
5278
  const targetAttrs = graph.getNodeAttributes(target);
4846
- const sourceDir = dirname9(sourceAttrs.filePath);
4847
- const targetDir = dirname9(targetAttrs.filePath);
5279
+ const sourceDir = dirname10(sourceAttrs.filePath);
5280
+ const targetDir = dirname10(targetAttrs.filePath);
4848
5281
  if (sourceDir !== targetDir) {
4849
5282
  if (!dirEdges.has(sourceDir)) {
4850
5283
  dirEdges.set(sourceDir, { in: 0, out: 0 });
@@ -4925,7 +5358,7 @@ function detectClusters(graph) {
4925
5358
  const dirFiles = /* @__PURE__ */ new Map();
4926
5359
  const fileEdges = /* @__PURE__ */ new Map();
4927
5360
  graph.forEachNode((node, attrs) => {
4928
- const dir = dirname9(attrs.filePath);
5361
+ const dir = dirname10(attrs.filePath);
4929
5362
  if (!dirFiles.has(dir)) {
4930
5363
  dirFiles.set(dir, /* @__PURE__ */ new Set());
4931
5364
  }
@@ -4979,8 +5412,8 @@ function inferClusterName(files) {
4979
5412
  if (sortedWords.length > 0 && sortedWords[0][1] > 1) {
4980
5413
  return capitalizeFirst2(sortedWords[0][0]);
4981
5414
  }
4982
- const commonDir = dirname9(files[0]);
4983
- if (files.every((f) => dirname9(f) === commonDir)) {
5415
+ const commonDir = dirname10(files[0]);
5416
+ if (files.every((f) => dirname10(f) === commonDir)) {
4984
5417
  return capitalizeFirst2(commonDir.split("/").pop() || "Core");
4985
5418
  }
4986
5419
  return "Core";
@@ -5038,7 +5471,7 @@ function generateDepwireUsage(projectRoot) {
5038
5471
  }
5039
5472
 
5040
5473
  // src/docs/files.ts
5041
- import { dirname as dirname10, basename as basename3 } from "path";
5474
+ import { dirname as dirname11, basename as basename3 } from "path";
5042
5475
  function generateFiles(graph, projectRoot, version) {
5043
5476
  let output = "";
5044
5477
  const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -5142,7 +5575,7 @@ function generateDirectoryBreakdown(graph) {
5142
5575
  const fileStats = getFileStats2(graph);
5143
5576
  const dirMap = /* @__PURE__ */ new Map();
5144
5577
  for (const file of fileStats) {
5145
- const dir = dirname10(file.filePath);
5578
+ const dir = dirname11(file.filePath);
5146
5579
  const topDir = dir === "." ? "." : dir.split("/")[0];
5147
5580
  if (!dirMap.has(topDir)) {
5148
5581
  dirMap.set(topDir, {
@@ -5785,7 +6218,7 @@ function generateRecommendations(graph) {
5785
6218
  }
5786
6219
 
5787
6220
  // src/docs/tests.ts
5788
- import { basename as basename4, dirname as dirname11 } from "path";
6221
+ import { basename as basename4, dirname as dirname12 } from "path";
5789
6222
  function generateTests(graph, projectRoot, version) {
5790
6223
  let output = "";
5791
6224
  const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -5814,7 +6247,7 @@ function getFileCount8(graph) {
5814
6247
  }
5815
6248
  function isTestFile3(filePath) {
5816
6249
  const fileName = basename4(filePath).toLowerCase();
5817
- const dirPath = dirname11(filePath).toLowerCase();
6250
+ const dirPath = dirname12(filePath).toLowerCase();
5818
6251
  if (dirPath.includes("test") || dirPath.includes("spec") || dirPath.includes("__tests__")) {
5819
6252
  return true;
5820
6253
  }
@@ -5873,12 +6306,12 @@ function generateTestFileInventory(graph) {
5873
6306
  }
5874
6307
  function matchTestToSource(testFile) {
5875
6308
  const testFileName = basename4(testFile);
5876
- const testDir = dirname11(testFile);
6309
+ const testDir = dirname12(testFile);
5877
6310
  let sourceFileName = testFileName.replace(/\.test\./g, ".").replace(/\.spec\./g, ".").replace(/_test\./g, ".").replace(/_spec\./g, ".");
5878
6311
  const possiblePaths = [];
5879
6312
  possiblePaths.push(testDir + "/" + sourceFileName);
5880
6313
  if (testDir.endsWith("/test") || testDir.endsWith("/tests") || testDir.endsWith("/__tests__")) {
5881
- const parentDir = dirname11(testDir);
6314
+ const parentDir = dirname12(testDir);
5882
6315
  possiblePaths.push(parentDir + "/" + sourceFileName);
5883
6316
  }
5884
6317
  if (testDir.includes("test")) {
@@ -6075,7 +6508,7 @@ function generateTestStatistics(graph) {
6075
6508
  `;
6076
6509
  const dirTestCoverage = /* @__PURE__ */ new Map();
6077
6510
  for (const sourceFile of sourceFiles) {
6078
- const dir = dirname11(sourceFile).split("/")[0];
6511
+ const dir = dirname12(sourceFile).split("/")[0];
6079
6512
  if (!dirTestCoverage.has(dir)) {
6080
6513
  dirTestCoverage.set(dir, { total: 0, tested: 0 });
6081
6514
  }
@@ -6098,7 +6531,7 @@ function generateTestStatistics(graph) {
6098
6531
  }
6099
6532
 
6100
6533
  // src/docs/history.ts
6101
- import { dirname as dirname12 } from "path";
6534
+ import { dirname as dirname13 } from "path";
6102
6535
  import { execSync } from "child_process";
6103
6536
  function generateHistory(graph, projectRoot, version) {
6104
6537
  let output = "";
@@ -6379,7 +6812,7 @@ function generateFeatureClusters(graph) {
6379
6812
  const dirFiles = /* @__PURE__ */ new Map();
6380
6813
  const fileEdges = /* @__PURE__ */ new Map();
6381
6814
  graph.forEachNode((node, attrs) => {
6382
- const dir = dirname12(attrs.filePath);
6815
+ const dir = dirname13(attrs.filePath);
6383
6816
  if (!dirFiles.has(dir)) {
6384
6817
  dirFiles.set(dir, /* @__PURE__ */ new Set());
6385
6818
  }
@@ -6461,7 +6894,7 @@ function capitalizeFirst3(str) {
6461
6894
  }
6462
6895
 
6463
6896
  // src/docs/current.ts
6464
- import { dirname as dirname13 } from "path";
6897
+ import { dirname as dirname14 } from "path";
6465
6898
  function generateCurrent(graph, projectRoot, version) {
6466
6899
  let output = "";
6467
6900
  const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -6599,7 +7032,7 @@ function generateCompleteFileIndex(graph) {
6599
7032
  fileInfos.sort((a, b) => a.filePath.localeCompare(b.filePath));
6600
7033
  const dirGroups = /* @__PURE__ */ new Map();
6601
7034
  for (const info of fileInfos) {
6602
- const dir = dirname13(info.filePath);
7035
+ const dir = dirname14(info.filePath);
6603
7036
  const topDir = dir === "." ? "root" : dir.split("/")[0];
6604
7037
  if (!dirGroups.has(topDir)) {
6605
7038
  dirGroups.set(topDir, []);
@@ -6810,8 +7243,8 @@ function getTopLevelDir2(filePath) {
6810
7243
  }
6811
7244
 
6812
7245
  // src/docs/status.ts
6813
- import { readFileSync as readFileSync5, existsSync as existsSync7 } from "fs";
6814
- import { join as join10 } from "path";
7246
+ import { readFileSync as readFileSync6, existsSync as existsSync8 } from "fs";
7247
+ import { join as join11 } from "path";
6815
7248
  function generateStatus(graph, projectRoot, version) {
6816
7249
  let output = "";
6817
7250
  const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -6844,12 +7277,12 @@ function getFileCount11(graph) {
6844
7277
  }
6845
7278
  function extractComments(projectRoot, filePath) {
6846
7279
  const comments = [];
6847
- const fullPath = join10(projectRoot, filePath);
6848
- if (!existsSync7(fullPath)) {
7280
+ const fullPath = join11(projectRoot, filePath);
7281
+ if (!existsSync8(fullPath)) {
6849
7282
  return comments;
6850
7283
  }
6851
7284
  try {
6852
- const content = readFileSync5(fullPath, "utf-8");
7285
+ const content = readFileSync6(fullPath, "utf-8");
6853
7286
  const lines = content.split("\n");
6854
7287
  const patterns = [
6855
7288
  { type: "TODO", regex: /(?:\/\/|#|\/\*)\s*TODO:?\s*(.+)/i },
@@ -7411,15 +7844,15 @@ function generateConfidenceSection(title, description, symbols, projectRoot) {
7411
7844
  }
7412
7845
 
7413
7846
  // src/docs/metadata.ts
7414
- import { existsSync as existsSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "fs";
7415
- import { join as join11 } from "path";
7847
+ import { existsSync as existsSync9, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "fs";
7848
+ import { join as join12 } from "path";
7416
7849
  function loadMetadata(outputDir) {
7417
- const metadataPath = join11(outputDir, "metadata.json");
7418
- if (!existsSync8(metadataPath)) {
7850
+ const metadataPath = join12(outputDir, "metadata.json");
7851
+ if (!existsSync9(metadataPath)) {
7419
7852
  return null;
7420
7853
  }
7421
7854
  try {
7422
- const content = readFileSync6(metadataPath, "utf-8");
7855
+ const content = readFileSync7(metadataPath, "utf-8");
7423
7856
  return JSON.parse(content);
7424
7857
  } catch (err) {
7425
7858
  console.error("Failed to load metadata:", err);
@@ -7427,7 +7860,7 @@ function loadMetadata(outputDir) {
7427
7860
  }
7428
7861
  }
7429
7862
  function saveMetadata(outputDir, metadata) {
7430
- const metadataPath = join11(outputDir, "metadata.json");
7863
+ const metadataPath = join12(outputDir, "metadata.json");
7431
7864
  writeFileSync2(metadataPath, JSON.stringify(metadata, null, 2), "utf-8");
7432
7865
  }
7433
7866
  function createMetadata(version, projectPath, fileCount, symbolCount, edgeCount, docTypes) {
@@ -7470,7 +7903,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
7470
7903
  const generated = [];
7471
7904
  const errors = [];
7472
7905
  try {
7473
- if (!existsSync9(options.outputDir)) {
7906
+ if (!existsSync10(options.outputDir)) {
7474
7907
  mkdirSync2(options.outputDir, { recursive: true });
7475
7908
  if (options.verbose) {
7476
7909
  console.log(`Created output directory: ${options.outputDir}`);
@@ -7509,7 +7942,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
7509
7942
  try {
7510
7943
  if (options.verbose) console.log("Generating ARCHITECTURE.md...");
7511
7944
  const content = generateArchitecture(graph, projectRoot, version, parseTime);
7512
- const filePath = join12(options.outputDir, "ARCHITECTURE.md");
7945
+ const filePath = join13(options.outputDir, "ARCHITECTURE.md");
7513
7946
  writeFileSync3(filePath, content, "utf-8");
7514
7947
  generated.push("ARCHITECTURE.md");
7515
7948
  } catch (err) {
@@ -7520,7 +7953,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
7520
7953
  try {
7521
7954
  if (options.verbose) console.log("Generating CONVENTIONS.md...");
7522
7955
  const content = generateConventions(graph, projectRoot, version);
7523
- const filePath = join12(options.outputDir, "CONVENTIONS.md");
7956
+ const filePath = join13(options.outputDir, "CONVENTIONS.md");
7524
7957
  writeFileSync3(filePath, content, "utf-8");
7525
7958
  generated.push("CONVENTIONS.md");
7526
7959
  } catch (err) {
@@ -7531,7 +7964,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
7531
7964
  try {
7532
7965
  if (options.verbose) console.log("Generating DEPENDENCIES.md...");
7533
7966
  const content = generateDependencies(graph, projectRoot, version);
7534
- const filePath = join12(options.outputDir, "DEPENDENCIES.md");
7967
+ const filePath = join13(options.outputDir, "DEPENDENCIES.md");
7535
7968
  writeFileSync3(filePath, content, "utf-8");
7536
7969
  generated.push("DEPENDENCIES.md");
7537
7970
  } catch (err) {
@@ -7542,7 +7975,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
7542
7975
  try {
7543
7976
  if (options.verbose) console.log("Generating ONBOARDING.md...");
7544
7977
  const content = generateOnboarding(graph, projectRoot, version);
7545
- const filePath = join12(options.outputDir, "ONBOARDING.md");
7978
+ const filePath = join13(options.outputDir, "ONBOARDING.md");
7546
7979
  writeFileSync3(filePath, content, "utf-8");
7547
7980
  generated.push("ONBOARDING.md");
7548
7981
  } catch (err) {
@@ -7553,7 +7986,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
7553
7986
  try {
7554
7987
  if (options.verbose) console.log("Generating FILES.md...");
7555
7988
  const content = generateFiles(graph, projectRoot, version);
7556
- const filePath = join12(options.outputDir, "FILES.md");
7989
+ const filePath = join13(options.outputDir, "FILES.md");
7557
7990
  writeFileSync3(filePath, content, "utf-8");
7558
7991
  generated.push("FILES.md");
7559
7992
  } catch (err) {
@@ -7564,7 +7997,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
7564
7997
  try {
7565
7998
  if (options.verbose) console.log("Generating API_SURFACE.md...");
7566
7999
  const content = generateApiSurface(graph, projectRoot, version);
7567
- const filePath = join12(options.outputDir, "API_SURFACE.md");
8000
+ const filePath = join13(options.outputDir, "API_SURFACE.md");
7568
8001
  writeFileSync3(filePath, content, "utf-8");
7569
8002
  generated.push("API_SURFACE.md");
7570
8003
  } catch (err) {
@@ -7575,7 +8008,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
7575
8008
  try {
7576
8009
  if (options.verbose) console.log("Generating ERRORS.md...");
7577
8010
  const content = generateErrors(graph, projectRoot, version);
7578
- const filePath = join12(options.outputDir, "ERRORS.md");
8011
+ const filePath = join13(options.outputDir, "ERRORS.md");
7579
8012
  writeFileSync3(filePath, content, "utf-8");
7580
8013
  generated.push("ERRORS.md");
7581
8014
  } catch (err) {
@@ -7586,7 +8019,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
7586
8019
  try {
7587
8020
  if (options.verbose) console.log("Generating TESTS.md...");
7588
8021
  const content = generateTests(graph, projectRoot, version);
7589
- const filePath = join12(options.outputDir, "TESTS.md");
8022
+ const filePath = join13(options.outputDir, "TESTS.md");
7590
8023
  writeFileSync3(filePath, content, "utf-8");
7591
8024
  generated.push("TESTS.md");
7592
8025
  } catch (err) {
@@ -7597,7 +8030,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
7597
8030
  try {
7598
8031
  if (options.verbose) console.log("Generating HISTORY.md...");
7599
8032
  const content = generateHistory(graph, projectRoot, version);
7600
- const filePath = join12(options.outputDir, "HISTORY.md");
8033
+ const filePath = join13(options.outputDir, "HISTORY.md");
7601
8034
  writeFileSync3(filePath, content, "utf-8");
7602
8035
  generated.push("HISTORY.md");
7603
8036
  } catch (err) {
@@ -7608,7 +8041,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
7608
8041
  try {
7609
8042
  if (options.verbose) console.log("Generating CURRENT.md...");
7610
8043
  const content = generateCurrent(graph, projectRoot, version);
7611
- const filePath = join12(options.outputDir, "CURRENT.md");
8044
+ const filePath = join13(options.outputDir, "CURRENT.md");
7612
8045
  writeFileSync3(filePath, content, "utf-8");
7613
8046
  generated.push("CURRENT.md");
7614
8047
  } catch (err) {
@@ -7619,7 +8052,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
7619
8052
  try {
7620
8053
  if (options.verbose) console.log("Generating STATUS.md...");
7621
8054
  const content = generateStatus(graph, projectRoot, version);
7622
- const filePath = join12(options.outputDir, "STATUS.md");
8055
+ const filePath = join13(options.outputDir, "STATUS.md");
7623
8056
  writeFileSync3(filePath, content, "utf-8");
7624
8057
  generated.push("STATUS.md");
7625
8058
  } catch (err) {
@@ -7630,7 +8063,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
7630
8063
  try {
7631
8064
  if (options.verbose) console.log("Generating HEALTH.md...");
7632
8065
  const content = generateHealth(graph, projectRoot, version);
7633
- const filePath = join12(options.outputDir, "HEALTH.md");
8066
+ const filePath = join13(options.outputDir, "HEALTH.md");
7634
8067
  writeFileSync3(filePath, content, "utf-8");
7635
8068
  generated.push("HEALTH.md");
7636
8069
  } catch (err) {
@@ -7641,7 +8074,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
7641
8074
  try {
7642
8075
  if (options.verbose) console.log("Generating DEAD_CODE.md...");
7643
8076
  const content = generateDeadCode(graph, projectRoot, version);
7644
- const filePath = join12(options.outputDir, "DEAD_CODE.md");
8077
+ const filePath = join13(options.outputDir, "DEAD_CODE.md");
7645
8078
  writeFileSync3(filePath, content, "utf-8");
7646
8079
  generated.push("DEAD_CODE.md");
7647
8080
  } catch (err) {
@@ -7689,13 +8122,13 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
7689
8122
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7690
8123
 
7691
8124
  // src/mcp/tools.ts
7692
- import { dirname as dirname14, join as join15 } from "path";
7693
- import { existsSync as existsSync12, readFileSync as readFileSync8 } from "fs";
8125
+ import { dirname as dirname15, join as join16 } from "path";
8126
+ import { existsSync as existsSync13, readFileSync as readFileSync9 } from "fs";
7694
8127
 
7695
8128
  // src/mcp/connect.ts
7696
8129
  import simpleGit from "simple-git";
7697
- import { existsSync as existsSync10 } from "fs";
7698
- import { join as join13, basename as basename5, resolve as resolve2 } from "path";
8130
+ import { existsSync as existsSync11 } from "fs";
8131
+ import { join as join14, basename as basename5, resolve as resolve2 } from "path";
7699
8132
  import { tmpdir, homedir } from "os";
7700
8133
  function validateProjectPath(source) {
7701
8134
  const resolved = resolve2(source);
@@ -7708,11 +8141,11 @@ function validateProjectPath(source) {
7708
8141
  "/boot",
7709
8142
  "/proc",
7710
8143
  "/sys",
7711
- join13(homedir(), ".ssh"),
7712
- join13(homedir(), ".gnupg"),
7713
- join13(homedir(), ".aws"),
7714
- join13(homedir(), ".config"),
7715
- join13(homedir(), ".env")
8144
+ join14(homedir(), ".ssh"),
8145
+ join14(homedir(), ".gnupg"),
8146
+ join14(homedir(), ".aws"),
8147
+ join14(homedir(), ".config"),
8148
+ join14(homedir(), ".env")
7716
8149
  ];
7717
8150
  for (const blocked of blockedPaths) {
7718
8151
  if (resolved.startsWith(blocked)) {
@@ -7735,11 +8168,11 @@ async function connectToRepo(source, subdirectory, state) {
7735
8168
  };
7736
8169
  }
7737
8170
  projectName = match[1];
7738
- const reposDir = join13(tmpdir(), "depwire-repos");
7739
- const cloneDir = join13(reposDir, projectName);
8171
+ const reposDir = join14(tmpdir(), "depwire-repos");
8172
+ const cloneDir = join14(reposDir, projectName);
7740
8173
  console.error(`Connecting to GitHub repo: ${source}`);
7741
8174
  const git = simpleGit();
7742
- if (existsSync10(cloneDir)) {
8175
+ if (existsSync11(cloneDir)) {
7743
8176
  console.error(`Repo already cloned at ${cloneDir}, pulling latest changes...`);
7744
8177
  try {
7745
8178
  await git.cwd(cloneDir).pull();
@@ -7757,7 +8190,7 @@ async function connectToRepo(source, subdirectory, state) {
7757
8190
  };
7758
8191
  }
7759
8192
  }
7760
- projectRoot = subdirectory ? join13(cloneDir, subdirectory) : cloneDir;
8193
+ projectRoot = subdirectory ? join14(cloneDir, subdirectory) : cloneDir;
7761
8194
  } else {
7762
8195
  const validation2 = validateProjectPath(source);
7763
8196
  if (!validation2.valid) {
@@ -7766,13 +8199,13 @@ async function connectToRepo(source, subdirectory, state) {
7766
8199
  message: validation2.error
7767
8200
  };
7768
8201
  }
7769
- if (!existsSync10(source)) {
8202
+ if (!existsSync11(source)) {
7770
8203
  return {
7771
8204
  error: "Directory not found",
7772
8205
  message: `Directory does not exist: ${source}`
7773
8206
  };
7774
8207
  }
7775
- projectRoot = subdirectory ? join13(source, subdirectory) : source;
8208
+ projectRoot = subdirectory ? join14(source, subdirectory) : source;
7776
8209
  projectName = basename5(projectRoot);
7777
8210
  }
7778
8211
  const validation = validateProjectPath(projectRoot);
@@ -7782,7 +8215,7 @@ async function connectToRepo(source, subdirectory, state) {
7782
8215
  message: validation.error
7783
8216
  };
7784
8217
  }
7785
- if (!existsSync10(projectRoot)) {
8218
+ if (!existsSync11(projectRoot)) {
7786
8219
  return {
7787
8220
  error: "Project root not found",
7788
8221
  message: `Directory does not exist: ${projectRoot}`
@@ -8063,24 +8496,24 @@ function getWeekNumber(date) {
8063
8496
  }
8064
8497
 
8065
8498
  // src/temporal/snapshots.ts
8066
- import { writeFileSync as writeFileSync4, readFileSync as readFileSync7, mkdirSync as mkdirSync3, existsSync as existsSync11, readdirSync as readdirSync3 } from "fs";
8067
- import { join as join14 } from "path";
8499
+ import { writeFileSync as writeFileSync4, readFileSync as readFileSync8, mkdirSync as mkdirSync3, existsSync as existsSync12, readdirSync as readdirSync4 } from "fs";
8500
+ import { join as join15 } from "path";
8068
8501
  function saveSnapshot(snapshot, outputDir) {
8069
- if (!existsSync11(outputDir)) {
8502
+ if (!existsSync12(outputDir)) {
8070
8503
  mkdirSync3(outputDir, { recursive: true });
8071
8504
  }
8072
8505
  const filename = `${snapshot.commitHash.substring(0, 8)}.json`;
8073
- const filepath = join14(outputDir, filename);
8506
+ const filepath = join15(outputDir, filename);
8074
8507
  writeFileSync4(filepath, JSON.stringify(snapshot, null, 2), "utf-8");
8075
8508
  }
8076
8509
  function loadSnapshot(commitHash, outputDir) {
8077
8510
  const shortHash = commitHash.substring(0, 8);
8078
- const filepath = join14(outputDir, `${shortHash}.json`);
8079
- if (!existsSync11(filepath)) {
8511
+ const filepath = join15(outputDir, `${shortHash}.json`);
8512
+ if (!existsSync12(filepath)) {
8080
8513
  return null;
8081
8514
  }
8082
8515
  try {
8083
- const content = readFileSync7(filepath, "utf-8");
8516
+ const content = readFileSync8(filepath, "utf-8");
8084
8517
  return JSON.parse(content);
8085
8518
  } catch {
8086
8519
  return null;
@@ -8767,7 +9200,7 @@ function handleGetArchitectureSummary(graph) {
8767
9200
  const dirMap = /* @__PURE__ */ new Map();
8768
9201
  const languageBreakdown = {};
8769
9202
  fileSummary.forEach((f) => {
8770
- const dir = f.filePath.includes("/") ? dirname14(f.filePath) : ".";
9203
+ const dir = f.filePath.includes("/") ? dirname15(f.filePath) : ".";
8771
9204
  if (!dirMap.has(dir)) {
8772
9205
  dirMap.set(dir, { fileCount: 0, symbolCount: 0 });
8773
9206
  }
@@ -8854,8 +9287,8 @@ The server will keep running until you end the MCP session or press Ctrl+C.`;
8854
9287
  };
8855
9288
  }
8856
9289
  async function handleGetProjectDocs(docType, state) {
8857
- const docsDir = join15(state.projectRoot, ".depwire");
8858
- if (!existsSync12(docsDir)) {
9290
+ const docsDir = join16(state.projectRoot, ".depwire");
9291
+ if (!existsSync13(docsDir)) {
8859
9292
  const errorMessage = `Project documentation has not been generated yet.
8860
9293
 
8861
9294
  Run \`depwire docs ${state.projectRoot}\` to generate codebase documentation.
@@ -8885,12 +9318,12 @@ Available document types:
8885
9318
  missing.push(doc);
8886
9319
  continue;
8887
9320
  }
8888
- const filePath = join15(docsDir, metadata.documents[doc].file);
8889
- if (!existsSync12(filePath)) {
9321
+ const filePath = join16(docsDir, metadata.documents[doc].file);
9322
+ if (!existsSync13(filePath)) {
8890
9323
  missing.push(doc);
8891
9324
  continue;
8892
9325
  }
8893
- const content = readFileSync8(filePath, "utf-8");
9326
+ const content = readFileSync9(filePath, "utf-8");
8894
9327
  if (docsToReturn.length > 1) {
8895
9328
  output += `
8896
9329
 
@@ -8915,16 +9348,16 @@ Available document types:
8915
9348
  }
8916
9349
  async function handleUpdateProjectDocs(docType, state) {
8917
9350
  const startTime = Date.now();
8918
- const docsDir = join15(state.projectRoot, ".depwire");
9351
+ const docsDir = join16(state.projectRoot, ".depwire");
8919
9352
  console.error("Regenerating project documentation...");
8920
9353
  const parsedFiles = await parseProject(state.projectRoot);
8921
9354
  const graph = buildGraph(parsedFiles);
8922
9355
  const parseTime = (Date.now() - startTime) / 1e3;
8923
9356
  state.graph = graph;
8924
- const packageJsonPath = join15(__dirname, "../../package.json");
8925
- const packageJson = JSON.parse(readFileSync8(packageJsonPath, "utf-8"));
9357
+ const packageJsonPath = join16(__dirname, "../../package.json");
9358
+ const packageJson = JSON.parse(readFileSync9(packageJsonPath, "utf-8"));
8926
9359
  const docsToGenerate = docType === "all" ? ["architecture", "conventions", "dependencies", "onboarding"] : [docType];
8927
- const docsExist = existsSync12(docsDir);
9360
+ const docsExist = existsSync13(docsDir);
8928
9361
  const result = await generateDocs(graph, state.projectRoot, packageJson.version, parseTime, {
8929
9362
  outputDir: docsDir,
8930
9363
  format: "markdown",
@@ -8983,7 +9416,7 @@ async function handleGetTemporalGraph(state, commits, strategy) {
8983
9416
  }
8984
9417
  const sampledCommits = sampleCommits(allCommits, commits, strategy);
8985
9418
  const snapshots = [];
8986
- const outputDir = join15(projectRoot, ".depwire", "temporal");
9419
+ const outputDir = join16(projectRoot, ".depwire", "temporal");
8987
9420
  for (const commit of sampledCommits) {
8988
9421
  const existing = loadSnapshot(commit.hash, outputDir);
8989
9422
  if (existing) {
package/dist/index.js CHANGED
@@ -27,7 +27,7 @@ import {
27
27
  stashChanges,
28
28
  updateFileInGraph,
29
29
  watchProject
30
- } from "./chunk-IGRFC3MQ.js";
30
+ } from "./chunk-MGMDHXPB.js";
31
31
 
32
32
  // src/index.ts
33
33
  import { Command } from "commander";
@@ -6,7 +6,7 @@ import {
6
6
  startMcpServer,
7
7
  updateFileInGraph,
8
8
  watchProject
9
- } from "./chunk-IGRFC3MQ.js";
9
+ } from "./chunk-MGMDHXPB.js";
10
10
 
11
11
  // src/mcpb-entry.ts
12
12
  import { resolve } from "path";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "depwire-cli",
3
- "version": "0.8.0",
4
- "description": "Code cross-reference visualization and AI context engine for TypeScript, JavaScript, Python, and Go. Zero native dependencies โ€” works on Windows, macOS, and Linux.",
3
+ "version": "0.9.0",
4
+ "description": "Code cross-reference visualization and AI context engine for TypeScript, JavaScript, Python, Go, and Rust. Zero native dependencies โ€” works on Windows, macOS, and Linux.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "depwire": "dist/index.js"