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
|
|
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
|
-
|
|
616
|
-
- [
|
|
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
|
-
- [ ]
|
|
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
|
-
|
|
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
|
|
84
|
-
import { join as
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
2708
|
-
const targetDir =
|
|
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 =
|
|
2756
|
-
const targetDir =
|
|
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
|
|
3039
|
-
import { join as
|
|
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 =
|
|
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 (
|
|
3583
|
+
if (existsSync7(historyFile)) {
|
|
3151
3584
|
try {
|
|
3152
|
-
const content =
|
|
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(
|
|
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 =
|
|
3166
|
-
if (!
|
|
3598
|
+
const historyFile = join10(projectRoot, ".depwire", "health-history.json");
|
|
3599
|
+
if (!existsSync7(historyFile)) {
|
|
3167
3600
|
return [];
|
|
3168
3601
|
}
|
|
3169
3602
|
try {
|
|
3170
|
-
const content =
|
|
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
|
|
3477
|
-
import { join as
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
3672
|
-
const targetDir =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
4847
|
-
const targetDir =
|
|
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 =
|
|
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 =
|
|
4983
|
-
if (files.every((f) =>
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
6814
|
-
import { join as
|
|
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 =
|
|
6848
|
-
if (!
|
|
7280
|
+
const fullPath = join11(projectRoot, filePath);
|
|
7281
|
+
if (!existsSync8(fullPath)) {
|
|
6849
7282
|
return comments;
|
|
6850
7283
|
}
|
|
6851
7284
|
try {
|
|
6852
|
-
const content =
|
|
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
|
|
7415
|
-
import { join as
|
|
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 =
|
|
7418
|
-
if (!
|
|
7850
|
+
const metadataPath = join12(outputDir, "metadata.json");
|
|
7851
|
+
if (!existsSync9(metadataPath)) {
|
|
7419
7852
|
return null;
|
|
7420
7853
|
}
|
|
7421
7854
|
try {
|
|
7422
|
-
const content =
|
|
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 =
|
|
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 (!
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
7693
|
-
import { existsSync as
|
|
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
|
|
7698
|
-
import { join as
|
|
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
|
-
|
|
7712
|
-
|
|
7713
|
-
|
|
7714
|
-
|
|
7715
|
-
|
|
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 =
|
|
7739
|
-
const cloneDir =
|
|
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 (
|
|
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 ?
|
|
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 (!
|
|
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 ?
|
|
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 (!
|
|
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
|
|
8067
|
-
import { join as
|
|
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 (!
|
|
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 =
|
|
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 =
|
|
8079
|
-
if (!
|
|
8511
|
+
const filepath = join15(outputDir, `${shortHash}.json`);
|
|
8512
|
+
if (!existsSync12(filepath)) {
|
|
8080
8513
|
return null;
|
|
8081
8514
|
}
|
|
8082
8515
|
try {
|
|
8083
|
-
const content =
|
|
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("/") ?
|
|
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 =
|
|
8858
|
-
if (!
|
|
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 =
|
|
8889
|
-
if (!
|
|
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 =
|
|
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 =
|
|
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 =
|
|
8925
|
-
const packageJson = JSON.parse(
|
|
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 =
|
|
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 =
|
|
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
package/dist/mcpb-entry.js
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "depwire-cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Code cross-reference visualization and AI context engine for TypeScript, JavaScript, Python, and
|
|
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"
|