depwire-cli 0.9.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -30,7 +30,8 @@ function scanDirectory(rootDir, baseDir = rootDir) {
|
|
|
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
|
-
|
|
33
|
+
const isC = entry.endsWith(".c") || entry.endsWith(".h");
|
|
34
|
+
if (isTypeScript || isJavaScript || isPython || isGo || isRust || isC) {
|
|
34
35
|
files.push(relative(rootDir, fullPath));
|
|
35
36
|
}
|
|
36
37
|
}
|
|
@@ -61,6 +62,12 @@ function findProjectRoot(startDir = process.cwd()) {
|
|
|
61
62
|
// Python (modern)
|
|
62
63
|
"setup.py",
|
|
63
64
|
// Python (legacy)
|
|
65
|
+
"Makefile",
|
|
66
|
+
// C/C++ (make-based)
|
|
67
|
+
"CMakeLists.txt",
|
|
68
|
+
// C/C++ (cmake-based)
|
|
69
|
+
"configure.ac",
|
|
70
|
+
// C/C++ (autotools)
|
|
64
71
|
".git"
|
|
65
72
|
// Any git repo
|
|
66
73
|
];
|
|
@@ -83,8 +90,8 @@ function findProjectRoot(startDir = process.cwd()) {
|
|
|
83
90
|
}
|
|
84
91
|
|
|
85
92
|
// src/parser/index.ts
|
|
86
|
-
import { readFileSync as
|
|
87
|
-
import { join as
|
|
93
|
+
import { readFileSync as readFileSync5, statSync as statSync2 } from "fs";
|
|
94
|
+
import { join as join8 } from "path";
|
|
88
95
|
|
|
89
96
|
// src/parser/detect.ts
|
|
90
97
|
import { extname as extname3 } from "path";
|
|
@@ -113,7 +120,8 @@ async function initParser() {
|
|
|
113
120
|
"javascript": "tree-sitter-javascript.wasm",
|
|
114
121
|
"python": "tree-sitter-python.wasm",
|
|
115
122
|
"go": "tree-sitter-go.wasm",
|
|
116
|
-
"rust": "tree-sitter-rust.wasm"
|
|
123
|
+
"rust": "tree-sitter-rust.wasm",
|
|
124
|
+
"c": "tree-sitter-c.wasm"
|
|
117
125
|
};
|
|
118
126
|
for (const [name, file] of Object.entries(grammarFiles)) {
|
|
119
127
|
const wasmPath = path.join(grammarsDir, file);
|
|
@@ -2392,13 +2400,383 @@ var rustParser = {
|
|
|
2392
2400
|
parseFile: parseRustFile
|
|
2393
2401
|
};
|
|
2394
2402
|
|
|
2403
|
+
// src/parser/c.ts
|
|
2404
|
+
import { existsSync as existsSync7 } from "fs";
|
|
2405
|
+
import { join as join7, dirname as dirname6, relative as relative4 } from "path";
|
|
2406
|
+
function parseCFile(filePath, sourceCode, projectRoot) {
|
|
2407
|
+
const parser = getParser("c");
|
|
2408
|
+
const tree = parser.parse(sourceCode, null, { bufferSize: 1024 * 1024 });
|
|
2409
|
+
const context = {
|
|
2410
|
+
filePath,
|
|
2411
|
+
projectRoot,
|
|
2412
|
+
sourceCode,
|
|
2413
|
+
symbols: [],
|
|
2414
|
+
edges: [],
|
|
2415
|
+
currentScope: []
|
|
2416
|
+
};
|
|
2417
|
+
walkNode6(tree.rootNode, context);
|
|
2418
|
+
return {
|
|
2419
|
+
filePath,
|
|
2420
|
+
symbols: context.symbols,
|
|
2421
|
+
edges: context.edges
|
|
2422
|
+
};
|
|
2423
|
+
}
|
|
2424
|
+
function walkNode6(node, context) {
|
|
2425
|
+
processNode6(node, context);
|
|
2426
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
2427
|
+
const child = node.child(i);
|
|
2428
|
+
if (child) {
|
|
2429
|
+
walkNode6(child, context);
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
function processNode6(node, context) {
|
|
2434
|
+
const type = node.type;
|
|
2435
|
+
switch (type) {
|
|
2436
|
+
case "function_definition":
|
|
2437
|
+
processFunctionDefinition2(node, context);
|
|
2438
|
+
break;
|
|
2439
|
+
case "struct_specifier":
|
|
2440
|
+
processStructSpecifier(node, context);
|
|
2441
|
+
break;
|
|
2442
|
+
case "enum_specifier":
|
|
2443
|
+
processEnumSpecifier(node, context);
|
|
2444
|
+
break;
|
|
2445
|
+
case "type_definition":
|
|
2446
|
+
processTypeDefinition(node, context);
|
|
2447
|
+
break;
|
|
2448
|
+
case "declaration":
|
|
2449
|
+
processDeclaration(node, context);
|
|
2450
|
+
break;
|
|
2451
|
+
case "preproc_def":
|
|
2452
|
+
case "preproc_function_def":
|
|
2453
|
+
processMacroDefinition(node, context);
|
|
2454
|
+
break;
|
|
2455
|
+
case "preproc_include":
|
|
2456
|
+
processIncludeDirective(node, context);
|
|
2457
|
+
break;
|
|
2458
|
+
case "call_expression":
|
|
2459
|
+
processCallExpression6(node, context);
|
|
2460
|
+
break;
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
function processFunctionDefinition2(node, context) {
|
|
2464
|
+
const declarator = node.childForFieldName("declarator");
|
|
2465
|
+
if (!declarator) return;
|
|
2466
|
+
const nameNode = extractFunctionName(declarator);
|
|
2467
|
+
if (!nameNode) return;
|
|
2468
|
+
const name = nodeText5(nameNode, context);
|
|
2469
|
+
const exported = !hasStorageClass(node, "static", context);
|
|
2470
|
+
const symbolId = `${context.filePath}::${name}`;
|
|
2471
|
+
context.symbols.push({
|
|
2472
|
+
id: symbolId,
|
|
2473
|
+
name,
|
|
2474
|
+
kind: "function",
|
|
2475
|
+
filePath: context.filePath,
|
|
2476
|
+
startLine: node.startPosition.row + 1,
|
|
2477
|
+
endLine: node.endPosition.row + 1,
|
|
2478
|
+
exported
|
|
2479
|
+
});
|
|
2480
|
+
context.currentScope.push(name);
|
|
2481
|
+
const body = node.childForFieldName("body");
|
|
2482
|
+
if (body) {
|
|
2483
|
+
walkNode6(body, context);
|
|
2484
|
+
}
|
|
2485
|
+
context.currentScope.pop();
|
|
2486
|
+
}
|
|
2487
|
+
function processStructSpecifier(node, context) {
|
|
2488
|
+
const parent = node.parent;
|
|
2489
|
+
let name = null;
|
|
2490
|
+
if (parent && parent.type === "type_definition") {
|
|
2491
|
+
const typedefName = parent.childForFieldName("declarator");
|
|
2492
|
+
if (typedefName) {
|
|
2493
|
+
name = extractIdentifierFromDeclarator(typedefName, context);
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
if (!name) {
|
|
2497
|
+
const nameNode = node.childForFieldName("name");
|
|
2498
|
+
if (nameNode) {
|
|
2499
|
+
name = nodeText5(nameNode, context);
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
if (!name) return;
|
|
2503
|
+
const symbolId = `${context.filePath}::${name}`;
|
|
2504
|
+
context.symbols.push({
|
|
2505
|
+
id: symbolId,
|
|
2506
|
+
name,
|
|
2507
|
+
kind: "class",
|
|
2508
|
+
filePath: context.filePath,
|
|
2509
|
+
startLine: node.startPosition.row + 1,
|
|
2510
|
+
endLine: node.endPosition.row + 1,
|
|
2511
|
+
exported: true
|
|
2512
|
+
});
|
|
2513
|
+
}
|
|
2514
|
+
function processEnumSpecifier(node, context) {
|
|
2515
|
+
const parent = node.parent;
|
|
2516
|
+
let name = null;
|
|
2517
|
+
if (parent && parent.type === "type_definition") {
|
|
2518
|
+
const typedefName = parent.childForFieldName("declarator");
|
|
2519
|
+
if (typedefName) {
|
|
2520
|
+
name = extractIdentifierFromDeclarator(typedefName, context);
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
if (!name) {
|
|
2524
|
+
const nameNode = node.childForFieldName("name");
|
|
2525
|
+
if (nameNode) {
|
|
2526
|
+
name = nodeText5(nameNode, context);
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
if (!name) return;
|
|
2530
|
+
const symbolId = `${context.filePath}::${name}`;
|
|
2531
|
+
context.symbols.push({
|
|
2532
|
+
id: symbolId,
|
|
2533
|
+
name,
|
|
2534
|
+
kind: "enum",
|
|
2535
|
+
filePath: context.filePath,
|
|
2536
|
+
startLine: node.startPosition.row + 1,
|
|
2537
|
+
endLine: node.endPosition.row + 1,
|
|
2538
|
+
exported: true
|
|
2539
|
+
});
|
|
2540
|
+
}
|
|
2541
|
+
function processTypeDefinition(node, context) {
|
|
2542
|
+
const typeNode = node.childForFieldName("type");
|
|
2543
|
+
if (!typeNode) return;
|
|
2544
|
+
if (typeNode.type === "struct_specifier" || typeNode.type === "enum_specifier") {
|
|
2545
|
+
return;
|
|
2546
|
+
}
|
|
2547
|
+
const declarator = node.childForFieldName("declarator");
|
|
2548
|
+
if (!declarator) return;
|
|
2549
|
+
const name = extractIdentifierFromDeclarator(declarator, context);
|
|
2550
|
+
if (!name) return;
|
|
2551
|
+
const symbolId = `${context.filePath}::${name}`;
|
|
2552
|
+
context.symbols.push({
|
|
2553
|
+
id: symbolId,
|
|
2554
|
+
name,
|
|
2555
|
+
kind: "type_alias",
|
|
2556
|
+
filePath: context.filePath,
|
|
2557
|
+
startLine: node.startPosition.row + 1,
|
|
2558
|
+
endLine: node.endPosition.row + 1,
|
|
2559
|
+
exported: true
|
|
2560
|
+
});
|
|
2561
|
+
}
|
|
2562
|
+
function processDeclaration(node, context) {
|
|
2563
|
+
if (context.currentScope.length > 0) {
|
|
2564
|
+
return;
|
|
2565
|
+
}
|
|
2566
|
+
const parent = node.parent;
|
|
2567
|
+
if (!parent || parent.type !== "translation_unit") {
|
|
2568
|
+
return;
|
|
2569
|
+
}
|
|
2570
|
+
const hasStatic = hasStorageClass(node, "static", context);
|
|
2571
|
+
const declarator = node.childForFieldName("declarator");
|
|
2572
|
+
if (!declarator) return;
|
|
2573
|
+
const name = extractIdentifierFromDeclarator(declarator, context);
|
|
2574
|
+
if (!name) return;
|
|
2575
|
+
const symbolId = `${context.filePath}::${name}`;
|
|
2576
|
+
context.symbols.push({
|
|
2577
|
+
id: symbolId,
|
|
2578
|
+
name,
|
|
2579
|
+
kind: "variable",
|
|
2580
|
+
filePath: context.filePath,
|
|
2581
|
+
startLine: node.startPosition.row + 1,
|
|
2582
|
+
endLine: node.endPosition.row + 1,
|
|
2583
|
+
exported: !hasStatic
|
|
2584
|
+
});
|
|
2585
|
+
}
|
|
2586
|
+
function processMacroDefinition(node, context) {
|
|
2587
|
+
const nameNode = node.childForFieldName("name");
|
|
2588
|
+
if (!nameNode) return;
|
|
2589
|
+
const name = nodeText5(nameNode, context);
|
|
2590
|
+
const kind = node.type === "preproc_function_def" ? "function" : "constant";
|
|
2591
|
+
const symbolId = `${context.filePath}::${name}`;
|
|
2592
|
+
context.symbols.push({
|
|
2593
|
+
id: symbolId,
|
|
2594
|
+
name,
|
|
2595
|
+
kind,
|
|
2596
|
+
filePath: context.filePath,
|
|
2597
|
+
startLine: node.startPosition.row + 1,
|
|
2598
|
+
endLine: node.endPosition.row + 1,
|
|
2599
|
+
exported: true
|
|
2600
|
+
});
|
|
2601
|
+
}
|
|
2602
|
+
function processIncludeDirective(node, context) {
|
|
2603
|
+
const pathNode = node.childForFieldName("path");
|
|
2604
|
+
if (!pathNode) return;
|
|
2605
|
+
const pathText = nodeText5(pathNode, context);
|
|
2606
|
+
const isLocalInclude = pathText.startsWith('"') && pathText.endsWith('"');
|
|
2607
|
+
if (!isLocalInclude) {
|
|
2608
|
+
return;
|
|
2609
|
+
}
|
|
2610
|
+
const includePath = pathText.slice(1, -1);
|
|
2611
|
+
const resolvedFiles = resolveIncludePath(includePath, context.filePath, context.projectRoot);
|
|
2612
|
+
if (resolvedFiles.length === 0) return;
|
|
2613
|
+
const sourceId = `${context.filePath}::__file__`;
|
|
2614
|
+
for (const targetPath of resolvedFiles) {
|
|
2615
|
+
const targetId = `${targetPath}::__file__`;
|
|
2616
|
+
context.edges.push({
|
|
2617
|
+
source: sourceId,
|
|
2618
|
+
target: targetId,
|
|
2619
|
+
kind: "imports",
|
|
2620
|
+
filePath: context.filePath,
|
|
2621
|
+
line: node.startPosition.row + 1
|
|
2622
|
+
});
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
function processCallExpression6(node, context) {
|
|
2626
|
+
if (context.currentScope.length === 0) return;
|
|
2627
|
+
const functionNode = node.childForFieldName("function");
|
|
2628
|
+
if (!functionNode) return;
|
|
2629
|
+
const calleeName = nodeText5(functionNode, context);
|
|
2630
|
+
const builtins = /* @__PURE__ */ new Set(["printf", "scanf", "malloc", "free", "memcpy", "strlen", "strcmp", "strcpy", "strcat"]);
|
|
2631
|
+
if (builtins.has(calleeName)) return;
|
|
2632
|
+
const callerId = getCurrentSymbolId6(context);
|
|
2633
|
+
if (!callerId) return;
|
|
2634
|
+
const calleeId = resolveSymbol5(calleeName, context);
|
|
2635
|
+
if (!calleeId) return;
|
|
2636
|
+
context.edges.push({
|
|
2637
|
+
source: callerId,
|
|
2638
|
+
target: calleeId,
|
|
2639
|
+
kind: "calls",
|
|
2640
|
+
filePath: context.filePath,
|
|
2641
|
+
line: node.startPosition.row + 1
|
|
2642
|
+
});
|
|
2643
|
+
}
|
|
2644
|
+
function resolveIncludePath(includePath, currentFile, projectRoot) {
|
|
2645
|
+
const currentFileAbs = join7(projectRoot, currentFile);
|
|
2646
|
+
const currentDir = dirname6(currentFileAbs);
|
|
2647
|
+
const possibleFiles = [
|
|
2648
|
+
join7(currentDir, includePath),
|
|
2649
|
+
join7(projectRoot, includePath)
|
|
2650
|
+
];
|
|
2651
|
+
const resolvedFiles = [];
|
|
2652
|
+
for (const absPath of possibleFiles) {
|
|
2653
|
+
if (existsSync7(absPath)) {
|
|
2654
|
+
const relPath = relative4(projectRoot, absPath);
|
|
2655
|
+
resolvedFiles.push(relPath);
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2658
|
+
return resolvedFiles;
|
|
2659
|
+
}
|
|
2660
|
+
function hasStorageClass(node, className, context) {
|
|
2661
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
2662
|
+
const child = node.child(i);
|
|
2663
|
+
if (child && child.type === "storage_class_specifier") {
|
|
2664
|
+
const text = nodeText5(child, context);
|
|
2665
|
+
if (text === className) {
|
|
2666
|
+
return true;
|
|
2667
|
+
}
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
let parent = node.parent;
|
|
2671
|
+
while (parent) {
|
|
2672
|
+
for (let i = 0; i < parent.childCount; i++) {
|
|
2673
|
+
const child = parent.child(i);
|
|
2674
|
+
if (child && child.type === "storage_class_specifier") {
|
|
2675
|
+
const text = nodeText5(child, context);
|
|
2676
|
+
if (text === className) {
|
|
2677
|
+
return true;
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
parent = parent.parent;
|
|
2682
|
+
}
|
|
2683
|
+
return false;
|
|
2684
|
+
}
|
|
2685
|
+
function extractFunctionName(declarator) {
|
|
2686
|
+
if (declarator.type === "identifier") {
|
|
2687
|
+
return declarator;
|
|
2688
|
+
}
|
|
2689
|
+
if (declarator.type === "function_declarator") {
|
|
2690
|
+
const innerDeclarator = declarator.childForFieldName("declarator");
|
|
2691
|
+
if (innerDeclarator) {
|
|
2692
|
+
return extractFunctionName(innerDeclarator);
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
if (declarator.type === "pointer_declarator") {
|
|
2696
|
+
const innerDeclarator = declarator.childForFieldName("declarator");
|
|
2697
|
+
if (innerDeclarator) {
|
|
2698
|
+
return extractFunctionName(innerDeclarator);
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
for (let i = 0; i < declarator.childCount; i++) {
|
|
2702
|
+
const child = declarator.child(i);
|
|
2703
|
+
if (child && child.type === "identifier") {
|
|
2704
|
+
return child;
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
return null;
|
|
2708
|
+
}
|
|
2709
|
+
function extractIdentifierFromDeclarator(declarator, context) {
|
|
2710
|
+
if (declarator.type === "identifier") {
|
|
2711
|
+
return nodeText5(declarator, context);
|
|
2712
|
+
}
|
|
2713
|
+
if (declarator.type === "type_identifier") {
|
|
2714
|
+
return nodeText5(declarator, context);
|
|
2715
|
+
}
|
|
2716
|
+
const identifierNode = findChildByType6(declarator, "identifier");
|
|
2717
|
+
if (identifierNode) {
|
|
2718
|
+
return nodeText5(identifierNode, context);
|
|
2719
|
+
}
|
|
2720
|
+
const typeIdNode = findChildByType6(declarator, "type_identifier");
|
|
2721
|
+
if (typeIdNode) {
|
|
2722
|
+
return nodeText5(typeIdNode, context);
|
|
2723
|
+
}
|
|
2724
|
+
for (let i = 0; i < declarator.childCount; i++) {
|
|
2725
|
+
const child = declarator.child(i);
|
|
2726
|
+
if (child) {
|
|
2727
|
+
const name = extractIdentifierFromDeclarator(child, context);
|
|
2728
|
+
if (name) return name;
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2731
|
+
return null;
|
|
2732
|
+
}
|
|
2733
|
+
function resolveSymbol5(name, context) {
|
|
2734
|
+
const currentFileId = `${context.filePath}::__file__`;
|
|
2735
|
+
const symbol = context.symbols.find(
|
|
2736
|
+
(s) => s.name === name && (s.filePath === context.filePath || s.exported)
|
|
2737
|
+
);
|
|
2738
|
+
if (symbol) {
|
|
2739
|
+
return symbol.id;
|
|
2740
|
+
}
|
|
2741
|
+
for (let i = context.currentScope.length - 1; i >= 0; i--) {
|
|
2742
|
+
const scopedId = `${context.filePath}::${context.currentScope.slice(0, i + 1).join("::")}::${name}`;
|
|
2743
|
+
const scopedSymbol = context.symbols.find((s) => s.id === scopedId);
|
|
2744
|
+
if (scopedSymbol) {
|
|
2745
|
+
return scopedSymbol.id;
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
return null;
|
|
2749
|
+
}
|
|
2750
|
+
function findChildByType6(node, type) {
|
|
2751
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
2752
|
+
const child = node.child(i);
|
|
2753
|
+
if (child && child.type === type) {
|
|
2754
|
+
return child;
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
return null;
|
|
2758
|
+
}
|
|
2759
|
+
function nodeText5(node, context) {
|
|
2760
|
+
return context.sourceCode.slice(node.startIndex, node.endIndex);
|
|
2761
|
+
}
|
|
2762
|
+
function getCurrentSymbolId6(context) {
|
|
2763
|
+
if (context.currentScope.length === 0) return null;
|
|
2764
|
+
return `${context.filePath}::${context.currentScope.join("::")}`;
|
|
2765
|
+
}
|
|
2766
|
+
var cParser = {
|
|
2767
|
+
name: "c",
|
|
2768
|
+
extensions: [".c", ".h"],
|
|
2769
|
+
parseFile: parseCFile
|
|
2770
|
+
};
|
|
2771
|
+
|
|
2395
2772
|
// src/parser/detect.ts
|
|
2396
2773
|
var parsers = [
|
|
2397
2774
|
typescriptParser,
|
|
2398
2775
|
pythonParser,
|
|
2399
2776
|
javascriptParser,
|
|
2400
2777
|
goParser,
|
|
2401
|
-
rustParser
|
|
2778
|
+
rustParser,
|
|
2779
|
+
cParser
|
|
2402
2780
|
];
|
|
2403
2781
|
function getParserForFile(filePath) {
|
|
2404
2782
|
const ext = extname3(filePath).toLowerCase();
|
|
@@ -2428,7 +2806,7 @@ async function parseProject(projectRoot, options) {
|
|
|
2428
2806
|
let errorFiles = 0;
|
|
2429
2807
|
for (const file of files) {
|
|
2430
2808
|
try {
|
|
2431
|
-
const fullPath =
|
|
2809
|
+
const fullPath = join8(projectRoot, file);
|
|
2432
2810
|
if (options?.exclude) {
|
|
2433
2811
|
const shouldExclude2 = options.exclude.some(
|
|
2434
2812
|
(pattern) => minimatch(file, pattern, { matchBase: true })
|
|
@@ -2454,7 +2832,7 @@ async function parseProject(projectRoot, options) {
|
|
|
2454
2832
|
skippedFiles++;
|
|
2455
2833
|
continue;
|
|
2456
2834
|
}
|
|
2457
|
-
const sourceCode =
|
|
2835
|
+
const sourceCode = readFileSync5(fullPath, "utf-8");
|
|
2458
2836
|
const parsed = parser.parseFile(file, sourceCode, projectRoot);
|
|
2459
2837
|
parsedFiles.push(parsed);
|
|
2460
2838
|
} catch (err) {
|
|
@@ -2847,7 +3225,7 @@ function watchProject(projectRoot, callbacks) {
|
|
|
2847
3225
|
const watcher = chokidar.watch(projectRoot, watcherOptions);
|
|
2848
3226
|
console.error("[Watcher] Attaching event listeners...");
|
|
2849
3227
|
watcher.on("change", (absolutePath) => {
|
|
2850
|
-
const validExtensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".go", ".rs"];
|
|
3228
|
+
const validExtensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".go", ".rs", ".c", ".h"];
|
|
2851
3229
|
if (!validExtensions.some((ext) => absolutePath.endsWith(ext))) return;
|
|
2852
3230
|
if (absolutePath.endsWith("_test.go")) return;
|
|
2853
3231
|
const relativePath = absolutePath.replace(projectRoot + "/", "");
|
|
@@ -2855,7 +3233,7 @@ function watchProject(projectRoot, callbacks) {
|
|
|
2855
3233
|
callbacks.onFileChanged(relativePath);
|
|
2856
3234
|
});
|
|
2857
3235
|
watcher.on("add", (absolutePath) => {
|
|
2858
|
-
const validExtensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".go", ".rs"];
|
|
3236
|
+
const validExtensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".go", ".rs", ".c", ".h"];
|
|
2859
3237
|
if (!validExtensions.some((ext) => absolutePath.endsWith(ext))) return;
|
|
2860
3238
|
if (absolutePath.endsWith("_test.go")) return;
|
|
2861
3239
|
const relativePath = absolutePath.replace(projectRoot + "/", "");
|
|
@@ -2863,7 +3241,7 @@ function watchProject(projectRoot, callbacks) {
|
|
|
2863
3241
|
callbacks.onFileAdded(relativePath);
|
|
2864
3242
|
});
|
|
2865
3243
|
watcher.on("unlink", (absolutePath) => {
|
|
2866
|
-
const validExtensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".go", ".rs"];
|
|
3244
|
+
const validExtensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".go", ".rs", ".c", ".h"];
|
|
2867
3245
|
if (!validExtensions.some((ext) => absolutePath.endsWith(ext))) return;
|
|
2868
3246
|
if (absolutePath.endsWith("_test.go")) return;
|
|
2869
3247
|
const relativePath = absolutePath.replace(projectRoot + "/", "");
|
|
@@ -2881,10 +3259,10 @@ function watchProject(projectRoot, callbacks) {
|
|
|
2881
3259
|
for (const dir of dirs) {
|
|
2882
3260
|
const files = watched[dir];
|
|
2883
3261
|
fileCount += files.filter(
|
|
2884
|
-
(f) => f.endsWith(".ts") || f.endsWith(".tsx") || f.endsWith(".js") || f.endsWith(".jsx") || f.endsWith(".mjs") || f.endsWith(".cjs") || f.endsWith(".py") || f.endsWith(".go") && !f.endsWith("_test.go")
|
|
3262
|
+
(f) => f.endsWith(".ts") || f.endsWith(".tsx") || f.endsWith(".js") || f.endsWith(".jsx") || f.endsWith(".mjs") || f.endsWith(".cjs") || f.endsWith(".py") || f.endsWith(".go") && !f.endsWith("_test.go") || f.endsWith(".rs") || f.endsWith(".c") || f.endsWith(".h")
|
|
2885
3263
|
).length;
|
|
2886
3264
|
}
|
|
2887
|
-
console.error(`[Watcher] Watching ${fileCount} TypeScript/JavaScript/Python/Go files in ${dirs.length} directories`);
|
|
3265
|
+
console.error(`[Watcher] Watching ${fileCount} TypeScript/JavaScript/Python/Go/Rust/C files in ${dirs.length} directories`);
|
|
2888
3266
|
});
|
|
2889
3267
|
return watcher;
|
|
2890
3268
|
}
|
|
@@ -2893,10 +3271,10 @@ function watchProject(projectRoot, callbacks) {
|
|
|
2893
3271
|
import express from "express";
|
|
2894
3272
|
import open from "open";
|
|
2895
3273
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2896
|
-
import { dirname as
|
|
3274
|
+
import { dirname as dirname7, join as join9 } from "path";
|
|
2897
3275
|
import { WebSocketServer } from "ws";
|
|
2898
3276
|
var __filename = fileURLToPath2(import.meta.url);
|
|
2899
|
-
var __dirname2 =
|
|
3277
|
+
var __dirname2 = dirname7(__filename);
|
|
2900
3278
|
var activeServer = null;
|
|
2901
3279
|
async function findAvailablePort(startPort, maxAttempts = 10) {
|
|
2902
3280
|
const net = await import("net");
|
|
@@ -2934,7 +3312,7 @@ async function startVizServer(initialVizData, graph, projectRoot, port = 3333, s
|
|
|
2934
3312
|
const availablePort = await findAvailablePort(port);
|
|
2935
3313
|
const app = express();
|
|
2936
3314
|
let vizData = initialVizData;
|
|
2937
|
-
const publicDir =
|
|
3315
|
+
const publicDir = join9(__dirname2, "viz", "public");
|
|
2938
3316
|
app.use(express.static(publicDir));
|
|
2939
3317
|
app.get("/api/graph", (req, res) => {
|
|
2940
3318
|
res.json(vizData);
|
|
@@ -3050,7 +3428,7 @@ function isProjectLoaded(state) {
|
|
|
3050
3428
|
}
|
|
3051
3429
|
|
|
3052
3430
|
// src/graph/updater.ts
|
|
3053
|
-
import { join as
|
|
3431
|
+
import { join as join10 } from "path";
|
|
3054
3432
|
function removeFileFromGraph(graph, filePath) {
|
|
3055
3433
|
const nodesToRemove = [];
|
|
3056
3434
|
graph.forEachNode((node, attrs) => {
|
|
@@ -3094,7 +3472,7 @@ function addFileToGraph(graph, parsedFile) {
|
|
|
3094
3472
|
}
|
|
3095
3473
|
async function updateFileInGraph(graph, projectRoot, relativeFilePath) {
|
|
3096
3474
|
removeFileFromGraph(graph, relativeFilePath);
|
|
3097
|
-
const absolutePath =
|
|
3475
|
+
const absolutePath = join10(projectRoot, relativeFilePath);
|
|
3098
3476
|
try {
|
|
3099
3477
|
const parsedFile = parseTypeScriptFile(absolutePath, relativeFilePath);
|
|
3100
3478
|
addFileToGraph(graph, parsedFile);
|
|
@@ -3104,7 +3482,7 @@ async function updateFileInGraph(graph, projectRoot, relativeFilePath) {
|
|
|
3104
3482
|
}
|
|
3105
3483
|
|
|
3106
3484
|
// src/health/metrics.ts
|
|
3107
|
-
import { dirname as
|
|
3485
|
+
import { dirname as dirname8 } from "path";
|
|
3108
3486
|
function scoreToGrade(score) {
|
|
3109
3487
|
if (score >= 90) return "A";
|
|
3110
3488
|
if (score >= 80) return "B";
|
|
@@ -3137,8 +3515,8 @@ function calculateCouplingScore(graph) {
|
|
|
3137
3515
|
totalEdges++;
|
|
3138
3516
|
fileConnections.set(sourceAttrs.filePath, (fileConnections.get(sourceAttrs.filePath) || 0) + 1);
|
|
3139
3517
|
fileConnections.set(targetAttrs.filePath, (fileConnections.get(targetAttrs.filePath) || 0) + 1);
|
|
3140
|
-
const sourceDir =
|
|
3141
|
-
const targetDir =
|
|
3518
|
+
const sourceDir = dirname8(sourceAttrs.filePath).split("/")[0];
|
|
3519
|
+
const targetDir = dirname8(targetAttrs.filePath).split("/")[0];
|
|
3142
3520
|
if (sourceDir !== targetDir) {
|
|
3143
3521
|
crossDirEdges++;
|
|
3144
3522
|
}
|
|
@@ -3185,8 +3563,8 @@ function calculateCohesionScore(graph) {
|
|
|
3185
3563
|
const sourceAttrs = graph.getNodeAttributes(source);
|
|
3186
3564
|
const targetAttrs = graph.getNodeAttributes(target);
|
|
3187
3565
|
if (sourceAttrs.filePath !== targetAttrs.filePath) {
|
|
3188
|
-
const sourceDir =
|
|
3189
|
-
const targetDir =
|
|
3566
|
+
const sourceDir = dirname8(sourceAttrs.filePath);
|
|
3567
|
+
const targetDir = dirname8(targetAttrs.filePath);
|
|
3190
3568
|
if (!dirEdges.has(sourceDir)) {
|
|
3191
3569
|
dirEdges.set(sourceDir, { internal: 0, total: 0 });
|
|
3192
3570
|
}
|
|
@@ -3468,8 +3846,8 @@ function calculateDepthScore(graph) {
|
|
|
3468
3846
|
}
|
|
3469
3847
|
|
|
3470
3848
|
// src/health/index.ts
|
|
3471
|
-
import { readFileSync as
|
|
3472
|
-
import { join as
|
|
3849
|
+
import { readFileSync as readFileSync6, writeFileSync, existsSync as existsSync8, mkdirSync } from "fs";
|
|
3850
|
+
import { join as join11, dirname as dirname9 } from "path";
|
|
3473
3851
|
function calculateHealthScore(graph, projectRoot) {
|
|
3474
3852
|
const coupling = calculateCouplingScore(graph);
|
|
3475
3853
|
const cohesion = calculateCohesionScore(graph);
|
|
@@ -3568,7 +3946,7 @@ function getHealthTrend(projectRoot, currentScore) {
|
|
|
3568
3946
|
}
|
|
3569
3947
|
}
|
|
3570
3948
|
function saveHealthHistory(projectRoot, report) {
|
|
3571
|
-
const historyFile =
|
|
3949
|
+
const historyFile = join11(projectRoot, ".depwire", "health-history.json");
|
|
3572
3950
|
const entry = {
|
|
3573
3951
|
timestamp: report.timestamp,
|
|
3574
3952
|
score: report.overall,
|
|
@@ -3580,9 +3958,9 @@ function saveHealthHistory(projectRoot, report) {
|
|
|
3580
3958
|
}))
|
|
3581
3959
|
};
|
|
3582
3960
|
let history = [];
|
|
3583
|
-
if (
|
|
3961
|
+
if (existsSync8(historyFile)) {
|
|
3584
3962
|
try {
|
|
3585
|
-
const content =
|
|
3963
|
+
const content = readFileSync6(historyFile, "utf-8");
|
|
3586
3964
|
history = JSON.parse(content);
|
|
3587
3965
|
} catch {
|
|
3588
3966
|
}
|
|
@@ -3591,16 +3969,16 @@ function saveHealthHistory(projectRoot, report) {
|
|
|
3591
3969
|
if (history.length > 50) {
|
|
3592
3970
|
history = history.slice(-50);
|
|
3593
3971
|
}
|
|
3594
|
-
mkdirSync(
|
|
3972
|
+
mkdirSync(dirname9(historyFile), { recursive: true });
|
|
3595
3973
|
writeFileSync(historyFile, JSON.stringify(history, null, 2), "utf-8");
|
|
3596
3974
|
}
|
|
3597
3975
|
function loadHealthHistory(projectRoot) {
|
|
3598
|
-
const historyFile =
|
|
3599
|
-
if (!
|
|
3976
|
+
const historyFile = join11(projectRoot, ".depwire", "health-history.json");
|
|
3977
|
+
if (!existsSync8(historyFile)) {
|
|
3600
3978
|
return [];
|
|
3601
3979
|
}
|
|
3602
3980
|
try {
|
|
3603
|
-
const content =
|
|
3981
|
+
const content = readFileSync6(historyFile, "utf-8");
|
|
3604
3982
|
return JSON.parse(content);
|
|
3605
3983
|
} catch {
|
|
3606
3984
|
return [];
|
|
@@ -3609,21 +3987,89 @@ function loadHealthHistory(projectRoot) {
|
|
|
3609
3987
|
|
|
3610
3988
|
// src/dead-code/detector.ts
|
|
3611
3989
|
import path2 from "path";
|
|
3612
|
-
|
|
3990
|
+
import { readFileSync as readFileSync7, existsSync as existsSync9 } from "fs";
|
|
3991
|
+
function findDeadSymbols(graph, projectRoot, includeTests = false, debug = false) {
|
|
3613
3992
|
const deadSymbols = [];
|
|
3614
3993
|
const context = { graph, projectRoot };
|
|
3994
|
+
const stats = {
|
|
3995
|
+
total: 0,
|
|
3996
|
+
excludedByTestFile: 0,
|
|
3997
|
+
excludedByEntryPoint: 0,
|
|
3998
|
+
excludedByConfigFile: 0,
|
|
3999
|
+
excludedByTypeDeclaration: 0,
|
|
4000
|
+
excludedByDefaultExport: 0,
|
|
4001
|
+
excludedByFrameworkDir: 0
|
|
4002
|
+
};
|
|
4003
|
+
const packageEntryPoints = getPackageEntryPoints(projectRoot);
|
|
4004
|
+
if (debug) {
|
|
4005
|
+
console.log("\n\u{1F50D} Debug: Graph Structure");
|
|
4006
|
+
console.log(`Total nodes in graph: ${graph.order}`);
|
|
4007
|
+
console.log(`Total edges in graph: ${graph.size}`);
|
|
4008
|
+
let nodesWithZeroInDegree = 0;
|
|
4009
|
+
let nodesWithZeroOutDegree = 0;
|
|
4010
|
+
graph.forEachNode((node) => {
|
|
4011
|
+
if (graph.inDegree(node) === 0) nodesWithZeroInDegree++;
|
|
4012
|
+
if (graph.outDegree(node) === 0) nodesWithZeroOutDegree++;
|
|
4013
|
+
});
|
|
4014
|
+
console.log(`Nodes with inDegree=0: ${nodesWithZeroInDegree}`);
|
|
4015
|
+
console.log(`Nodes with outDegree=0: ${nodesWithZeroOutDegree}`);
|
|
4016
|
+
if (nodesWithZeroInDegree <= 10) {
|
|
4017
|
+
console.log("\nSample nodes with inDegree=0:");
|
|
4018
|
+
let count = 0;
|
|
4019
|
+
graph.forEachNode((node) => {
|
|
4020
|
+
if (graph.inDegree(node) === 0 && count < 10) {
|
|
4021
|
+
const attrs = graph.getNodeAttributes(node);
|
|
4022
|
+
const filePath = attrs.file || attrs.filePath || "unknown";
|
|
4023
|
+
console.log(` - ${attrs.name} (${attrs.kind}) in ${path2.relative(projectRoot, filePath)}`);
|
|
4024
|
+
count++;
|
|
4025
|
+
}
|
|
4026
|
+
});
|
|
4027
|
+
}
|
|
4028
|
+
}
|
|
3615
4029
|
for (const node of graph.nodes()) {
|
|
3616
4030
|
const attrs = graph.getNodeAttributes(node);
|
|
3617
|
-
if (!attrs.
|
|
4031
|
+
if (!attrs.name) continue;
|
|
4032
|
+
if (!attrs.file && !attrs.filePath) {
|
|
4033
|
+
if (debug) {
|
|
4034
|
+
console.log(`Skipping node ${attrs.name} - no file attribute`);
|
|
4035
|
+
}
|
|
4036
|
+
continue;
|
|
4037
|
+
}
|
|
4038
|
+
const filePath = attrs.file || attrs.filePath;
|
|
4039
|
+
if (!isRelevantForDeadCodeDetection(attrs)) {
|
|
4040
|
+
continue;
|
|
4041
|
+
}
|
|
3618
4042
|
const inDegree = graph.inDegree(node);
|
|
3619
4043
|
if (inDegree === 0) {
|
|
3620
|
-
|
|
4044
|
+
stats.total++;
|
|
4045
|
+
const exclusionReason = shouldExclude(attrs, context, includeTests, packageEntryPoints);
|
|
4046
|
+
if (exclusionReason) {
|
|
4047
|
+
switch (exclusionReason) {
|
|
4048
|
+
case "test":
|
|
4049
|
+
stats.excludedByTestFile++;
|
|
4050
|
+
break;
|
|
4051
|
+
case "entry":
|
|
4052
|
+
stats.excludedByEntryPoint++;
|
|
4053
|
+
break;
|
|
4054
|
+
case "config":
|
|
4055
|
+
stats.excludedByConfigFile++;
|
|
4056
|
+
break;
|
|
4057
|
+
case "types":
|
|
4058
|
+
stats.excludedByTypeDeclaration++;
|
|
4059
|
+
break;
|
|
4060
|
+
case "default":
|
|
4061
|
+
stats.excludedByDefaultExport++;
|
|
4062
|
+
break;
|
|
4063
|
+
case "framework":
|
|
4064
|
+
stats.excludedByFrameworkDir++;
|
|
4065
|
+
break;
|
|
4066
|
+
}
|
|
3621
4067
|
continue;
|
|
3622
4068
|
}
|
|
3623
4069
|
deadSymbols.push({
|
|
3624
4070
|
name: attrs.name,
|
|
3625
4071
|
kind: attrs.kind || "unknown",
|
|
3626
|
-
file:
|
|
4072
|
+
file: filePath,
|
|
3627
4073
|
line: attrs.startLine || 0,
|
|
3628
4074
|
exported: attrs.exported || false,
|
|
3629
4075
|
dependents: 0,
|
|
@@ -3632,38 +4078,115 @@ function findDeadSymbols(graph, projectRoot, includeTests = false) {
|
|
|
3632
4078
|
});
|
|
3633
4079
|
}
|
|
3634
4080
|
}
|
|
3635
|
-
|
|
4081
|
+
if (debug) {
|
|
4082
|
+
console.log("\n\u{1F50D} Debug: Exclusion Statistics");
|
|
4083
|
+
console.log(`Total symbols with 0 incoming edges: ${stats.total}`);
|
|
4084
|
+
console.log(`Excluded by test file: ${stats.excludedByTestFile}`);
|
|
4085
|
+
console.log(`Excluded by entry point: ${stats.excludedByEntryPoint}`);
|
|
4086
|
+
console.log(`Excluded by config file: ${stats.excludedByConfigFile}`);
|
|
4087
|
+
console.log(`Excluded by type declaration: ${stats.excludedByTypeDeclaration}`);
|
|
4088
|
+
console.log(`Excluded by default export: ${stats.excludedByDefaultExport}`);
|
|
4089
|
+
console.log(`Excluded by framework dir: ${stats.excludedByFrameworkDir}`);
|
|
4090
|
+
console.log(`Remaining dead symbols: ${deadSymbols.length}
|
|
4091
|
+
`);
|
|
4092
|
+
}
|
|
4093
|
+
return { symbols: deadSymbols, stats };
|
|
4094
|
+
}
|
|
4095
|
+
function isRelevantForDeadCodeDetection(attrs) {
|
|
4096
|
+
const kind = attrs.kind;
|
|
4097
|
+
const relevantKinds = [
|
|
4098
|
+
"function",
|
|
4099
|
+
"class",
|
|
4100
|
+
"interface",
|
|
4101
|
+
"type",
|
|
4102
|
+
"enum",
|
|
4103
|
+
"const",
|
|
4104
|
+
"let",
|
|
4105
|
+
"var",
|
|
4106
|
+
"method",
|
|
4107
|
+
"property"
|
|
4108
|
+
];
|
|
4109
|
+
if (!relevantKinds.includes(kind)) {
|
|
4110
|
+
return false;
|
|
4111
|
+
}
|
|
4112
|
+
if (kind === "const" || kind === "let" || kind === "var" || kind === "variable") {
|
|
4113
|
+
return attrs.exported === true;
|
|
4114
|
+
}
|
|
4115
|
+
return true;
|
|
4116
|
+
}
|
|
4117
|
+
function getPackageEntryPoints(projectRoot) {
|
|
4118
|
+
const entryPoints = /* @__PURE__ */ new Set();
|
|
4119
|
+
const packageJsonPath = path2.join(projectRoot, "package.json");
|
|
4120
|
+
if (!existsSync9(packageJsonPath)) {
|
|
4121
|
+
return entryPoints;
|
|
4122
|
+
}
|
|
4123
|
+
try {
|
|
4124
|
+
const packageJson = JSON.parse(readFileSync7(packageJsonPath, "utf-8"));
|
|
4125
|
+
if (packageJson.main) {
|
|
4126
|
+
entryPoints.add(path2.resolve(projectRoot, packageJson.main));
|
|
4127
|
+
}
|
|
4128
|
+
if (packageJson.module) {
|
|
4129
|
+
entryPoints.add(path2.resolve(projectRoot, packageJson.module));
|
|
4130
|
+
}
|
|
4131
|
+
if (packageJson.exports) {
|
|
4132
|
+
const addExports = (exp) => {
|
|
4133
|
+
if (typeof exp === "string") {
|
|
4134
|
+
entryPoints.add(path2.resolve(projectRoot, exp));
|
|
4135
|
+
} else if (typeof exp === "object") {
|
|
4136
|
+
for (const key in exp) {
|
|
4137
|
+
if (typeof exp[key] === "string") {
|
|
4138
|
+
entryPoints.add(path2.resolve(projectRoot, exp[key]));
|
|
4139
|
+
} else if (typeof exp[key] === "object") {
|
|
4140
|
+
addExports(exp[key]);
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
4143
|
+
}
|
|
4144
|
+
};
|
|
4145
|
+
addExports(packageJson.exports);
|
|
4146
|
+
}
|
|
4147
|
+
} catch (e) {
|
|
4148
|
+
}
|
|
4149
|
+
return entryPoints;
|
|
3636
4150
|
}
|
|
3637
|
-
function shouldExclude(attrs, context, includeTests) {
|
|
3638
|
-
const filePath = attrs.file;
|
|
4151
|
+
function shouldExclude(attrs, context, includeTests, packageEntryPoints) {
|
|
4152
|
+
const filePath = attrs.file || attrs.filePath;
|
|
4153
|
+
if (!filePath) {
|
|
4154
|
+
return null;
|
|
4155
|
+
}
|
|
3639
4156
|
const relativePath = path2.relative(context.projectRoot, filePath);
|
|
3640
4157
|
if (!includeTests && isTestFile(relativePath)) {
|
|
3641
|
-
return
|
|
4158
|
+
return "test";
|
|
3642
4159
|
}
|
|
3643
|
-
if (
|
|
3644
|
-
return
|
|
4160
|
+
if (isRealPackageEntryPoint(filePath, packageEntryPoints)) {
|
|
4161
|
+
return "entry";
|
|
3645
4162
|
}
|
|
3646
4163
|
if (isConfigFile(relativePath)) {
|
|
3647
|
-
return
|
|
4164
|
+
return "config";
|
|
3648
4165
|
}
|
|
3649
4166
|
if (isTypeDeclarationFile(relativePath)) {
|
|
3650
|
-
return
|
|
4167
|
+
return "types";
|
|
3651
4168
|
}
|
|
3652
4169
|
if (attrs.kind === "default") {
|
|
3653
|
-
return
|
|
4170
|
+
return "default";
|
|
3654
4171
|
}
|
|
3655
4172
|
if (isFrameworkAutoLoadedFile(relativePath)) {
|
|
3656
|
-
return
|
|
4173
|
+
return "framework";
|
|
4174
|
+
}
|
|
4175
|
+
return null;
|
|
4176
|
+
}
|
|
4177
|
+
function isRealPackageEntryPoint(filePath, packageEntryPoints) {
|
|
4178
|
+
const normalizedPath = path2.normalize(filePath);
|
|
4179
|
+
for (const entryPoint of packageEntryPoints) {
|
|
4180
|
+
const normalizedEntry = path2.normalize(entryPoint);
|
|
4181
|
+
if (normalizedPath === normalizedEntry || normalizedPath === normalizedEntry.replace(/\.(js|ts)$/, ".ts") || normalizedPath === normalizedEntry.replace(/\.(js|ts)$/, ".js")) {
|
|
4182
|
+
return true;
|
|
4183
|
+
}
|
|
3657
4184
|
}
|
|
3658
4185
|
return false;
|
|
3659
4186
|
}
|
|
3660
4187
|
function isTestFile(filePath) {
|
|
3661
4188
|
return filePath.includes("__tests__/") || filePath.includes(".test.") || filePath.includes(".spec.") || filePath.includes("/test/") || filePath.includes("/tests/");
|
|
3662
4189
|
}
|
|
3663
|
-
function isEntryPoint(filePath) {
|
|
3664
|
-
const basename6 = path2.basename(filePath);
|
|
3665
|
-
return basename6 === "index.ts" || basename6 === "index.js" || basename6 === "main.ts" || basename6 === "main.js" || basename6 === "app.ts" || basename6 === "app.js" || basename6 === "server.ts" || basename6 === "server.js";
|
|
3666
|
-
}
|
|
3667
4190
|
function isConfigFile(filePath) {
|
|
3668
4191
|
return filePath.includes(".config.") || filePath.includes("config/") || filePath.includes("vite.config") || filePath.includes("rollup.config") || filePath.includes("webpack.config");
|
|
3669
4192
|
}
|
|
@@ -3874,9 +4397,15 @@ function analyzeDeadCode(graph, projectRoot, options = {}) {
|
|
|
3874
4397
|
includeTests: options.includeTests || false,
|
|
3875
4398
|
verbose: options.verbose || false,
|
|
3876
4399
|
stats: options.stats || false,
|
|
3877
|
-
json: options.json || false
|
|
4400
|
+
json: options.json || false,
|
|
4401
|
+
debug: options.debug || false
|
|
3878
4402
|
};
|
|
3879
|
-
const rawDeadSymbols = findDeadSymbols(
|
|
4403
|
+
const { symbols: rawDeadSymbols } = findDeadSymbols(
|
|
4404
|
+
graph,
|
|
4405
|
+
projectRoot,
|
|
4406
|
+
opts.includeTests,
|
|
4407
|
+
opts.debug
|
|
4408
|
+
);
|
|
3880
4409
|
const classifiedSymbols = classifyDeadSymbols(rawDeadSymbols, graph);
|
|
3881
4410
|
const filteredSymbols = filterByConfidence(classifiedSymbols, opts.confidence);
|
|
3882
4411
|
const totalSymbols = graph.order;
|
|
@@ -3906,11 +4435,11 @@ function filterByConfidence(symbols, minConfidence) {
|
|
|
3906
4435
|
}
|
|
3907
4436
|
|
|
3908
4437
|
// src/docs/generator.ts
|
|
3909
|
-
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as
|
|
3910
|
-
import { join as
|
|
4438
|
+
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync12 } from "fs";
|
|
4439
|
+
import { join as join14 } from "path";
|
|
3911
4440
|
|
|
3912
4441
|
// src/docs/architecture.ts
|
|
3913
|
-
import { dirname as
|
|
4442
|
+
import { dirname as dirname10 } from "path";
|
|
3914
4443
|
|
|
3915
4444
|
// src/docs/templates.ts
|
|
3916
4445
|
function header(text, level = 1) {
|
|
@@ -4061,7 +4590,7 @@ function generateModuleStructure(graph) {
|
|
|
4061
4590
|
function getDirectoryStats(graph) {
|
|
4062
4591
|
const dirMap = /* @__PURE__ */ new Map();
|
|
4063
4592
|
graph.forEachNode((node, attrs) => {
|
|
4064
|
-
const dir =
|
|
4593
|
+
const dir = dirname10(attrs.filePath);
|
|
4065
4594
|
if (dir === ".") return;
|
|
4066
4595
|
if (!dirMap.has(dir)) {
|
|
4067
4596
|
dirMap.set(dir, {
|
|
@@ -4086,7 +4615,7 @@ function getDirectoryStats(graph) {
|
|
|
4086
4615
|
});
|
|
4087
4616
|
const filesPerDir = /* @__PURE__ */ new Map();
|
|
4088
4617
|
graph.forEachNode((node, attrs) => {
|
|
4089
|
-
const dir =
|
|
4618
|
+
const dir = dirname10(attrs.filePath);
|
|
4090
4619
|
if (!filesPerDir.has(dir)) {
|
|
4091
4620
|
filesPerDir.set(dir, /* @__PURE__ */ new Set());
|
|
4092
4621
|
}
|
|
@@ -4101,8 +4630,8 @@ function getDirectoryStats(graph) {
|
|
|
4101
4630
|
graph.forEachEdge((edge, attrs, source, target) => {
|
|
4102
4631
|
const sourceAttrs = graph.getNodeAttributes(source);
|
|
4103
4632
|
const targetAttrs = graph.getNodeAttributes(target);
|
|
4104
|
-
const sourceDir =
|
|
4105
|
-
const targetDir =
|
|
4633
|
+
const sourceDir = dirname10(sourceAttrs.filePath);
|
|
4634
|
+
const targetDir = dirname10(targetAttrs.filePath);
|
|
4106
4635
|
if (sourceDir !== targetDir) {
|
|
4107
4636
|
if (!dirEdges.has(sourceDir)) {
|
|
4108
4637
|
dirEdges.set(sourceDir, { in: 0, out: 0 });
|
|
@@ -5083,7 +5612,7 @@ function detectCyclesDetailed(graph) {
|
|
|
5083
5612
|
}
|
|
5084
5613
|
|
|
5085
5614
|
// src/docs/onboarding.ts
|
|
5086
|
-
import { dirname as
|
|
5615
|
+
import { dirname as dirname11 } from "path";
|
|
5087
5616
|
function generateOnboarding(graph, projectRoot, version) {
|
|
5088
5617
|
let output = "";
|
|
5089
5618
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -5142,7 +5671,7 @@ function generateQuickOrientation(graph) {
|
|
|
5142
5671
|
const primaryLang = Object.entries(languages2).sort((a, b) => b[1] - a[1])[0];
|
|
5143
5672
|
const dirs = /* @__PURE__ */ new Set();
|
|
5144
5673
|
graph.forEachNode((node, attrs) => {
|
|
5145
|
-
const dir =
|
|
5674
|
+
const dir = dirname11(attrs.filePath);
|
|
5146
5675
|
if (dir !== ".") {
|
|
5147
5676
|
const topLevel = dir.split("/")[0];
|
|
5148
5677
|
dirs.add(topLevel);
|
|
@@ -5246,7 +5775,7 @@ function generateModuleMap(graph) {
|
|
|
5246
5775
|
function getDirectoryStats2(graph) {
|
|
5247
5776
|
const dirMap = /* @__PURE__ */ new Map();
|
|
5248
5777
|
graph.forEachNode((node, attrs) => {
|
|
5249
|
-
const dir =
|
|
5778
|
+
const dir = dirname11(attrs.filePath);
|
|
5250
5779
|
if (dir === ".") return;
|
|
5251
5780
|
if (!dirMap.has(dir)) {
|
|
5252
5781
|
dirMap.set(dir, {
|
|
@@ -5261,7 +5790,7 @@ function getDirectoryStats2(graph) {
|
|
|
5261
5790
|
});
|
|
5262
5791
|
const filesPerDir = /* @__PURE__ */ new Map();
|
|
5263
5792
|
graph.forEachNode((node, attrs) => {
|
|
5264
|
-
const dir =
|
|
5793
|
+
const dir = dirname11(attrs.filePath);
|
|
5265
5794
|
if (!filesPerDir.has(dir)) {
|
|
5266
5795
|
filesPerDir.set(dir, /* @__PURE__ */ new Set());
|
|
5267
5796
|
}
|
|
@@ -5276,8 +5805,8 @@ function getDirectoryStats2(graph) {
|
|
|
5276
5805
|
graph.forEachEdge((edge, attrs, source, target) => {
|
|
5277
5806
|
const sourceAttrs = graph.getNodeAttributes(source);
|
|
5278
5807
|
const targetAttrs = graph.getNodeAttributes(target);
|
|
5279
|
-
const sourceDir =
|
|
5280
|
-
const targetDir =
|
|
5808
|
+
const sourceDir = dirname11(sourceAttrs.filePath);
|
|
5809
|
+
const targetDir = dirname11(targetAttrs.filePath);
|
|
5281
5810
|
if (sourceDir !== targetDir) {
|
|
5282
5811
|
if (!dirEdges.has(sourceDir)) {
|
|
5283
5812
|
dirEdges.set(sourceDir, { in: 0, out: 0 });
|
|
@@ -5358,7 +5887,7 @@ function detectClusters(graph) {
|
|
|
5358
5887
|
const dirFiles = /* @__PURE__ */ new Map();
|
|
5359
5888
|
const fileEdges = /* @__PURE__ */ new Map();
|
|
5360
5889
|
graph.forEachNode((node, attrs) => {
|
|
5361
|
-
const dir =
|
|
5890
|
+
const dir = dirname11(attrs.filePath);
|
|
5362
5891
|
if (!dirFiles.has(dir)) {
|
|
5363
5892
|
dirFiles.set(dir, /* @__PURE__ */ new Set());
|
|
5364
5893
|
}
|
|
@@ -5412,8 +5941,8 @@ function inferClusterName(files) {
|
|
|
5412
5941
|
if (sortedWords.length > 0 && sortedWords[0][1] > 1) {
|
|
5413
5942
|
return capitalizeFirst2(sortedWords[0][0]);
|
|
5414
5943
|
}
|
|
5415
|
-
const commonDir =
|
|
5416
|
-
if (files.every((f) =>
|
|
5944
|
+
const commonDir = dirname11(files[0]);
|
|
5945
|
+
if (files.every((f) => dirname11(f) === commonDir)) {
|
|
5417
5946
|
return capitalizeFirst2(commonDir.split("/").pop() || "Core");
|
|
5418
5947
|
}
|
|
5419
5948
|
return "Core";
|
|
@@ -5471,7 +6000,7 @@ function generateDepwireUsage(projectRoot) {
|
|
|
5471
6000
|
}
|
|
5472
6001
|
|
|
5473
6002
|
// src/docs/files.ts
|
|
5474
|
-
import { dirname as
|
|
6003
|
+
import { dirname as dirname12, basename as basename3 } from "path";
|
|
5475
6004
|
function generateFiles(graph, projectRoot, version) {
|
|
5476
6005
|
let output = "";
|
|
5477
6006
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -5575,7 +6104,7 @@ function generateDirectoryBreakdown(graph) {
|
|
|
5575
6104
|
const fileStats = getFileStats2(graph);
|
|
5576
6105
|
const dirMap = /* @__PURE__ */ new Map();
|
|
5577
6106
|
for (const file of fileStats) {
|
|
5578
|
-
const dir =
|
|
6107
|
+
const dir = dirname12(file.filePath);
|
|
5579
6108
|
const topDir = dir === "." ? "." : dir.split("/")[0];
|
|
5580
6109
|
if (!dirMap.has(topDir)) {
|
|
5581
6110
|
dirMap.set(topDir, {
|
|
@@ -6218,7 +6747,7 @@ function generateRecommendations(graph) {
|
|
|
6218
6747
|
}
|
|
6219
6748
|
|
|
6220
6749
|
// src/docs/tests.ts
|
|
6221
|
-
import { basename as basename4, dirname as
|
|
6750
|
+
import { basename as basename4, dirname as dirname13 } from "path";
|
|
6222
6751
|
function generateTests(graph, projectRoot, version) {
|
|
6223
6752
|
let output = "";
|
|
6224
6753
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -6247,7 +6776,7 @@ function getFileCount8(graph) {
|
|
|
6247
6776
|
}
|
|
6248
6777
|
function isTestFile3(filePath) {
|
|
6249
6778
|
const fileName = basename4(filePath).toLowerCase();
|
|
6250
|
-
const dirPath =
|
|
6779
|
+
const dirPath = dirname13(filePath).toLowerCase();
|
|
6251
6780
|
if (dirPath.includes("test") || dirPath.includes("spec") || dirPath.includes("__tests__")) {
|
|
6252
6781
|
return true;
|
|
6253
6782
|
}
|
|
@@ -6306,12 +6835,12 @@ function generateTestFileInventory(graph) {
|
|
|
6306
6835
|
}
|
|
6307
6836
|
function matchTestToSource(testFile) {
|
|
6308
6837
|
const testFileName = basename4(testFile);
|
|
6309
|
-
const testDir =
|
|
6838
|
+
const testDir = dirname13(testFile);
|
|
6310
6839
|
let sourceFileName = testFileName.replace(/\.test\./g, ".").replace(/\.spec\./g, ".").replace(/_test\./g, ".").replace(/_spec\./g, ".");
|
|
6311
6840
|
const possiblePaths = [];
|
|
6312
6841
|
possiblePaths.push(testDir + "/" + sourceFileName);
|
|
6313
6842
|
if (testDir.endsWith("/test") || testDir.endsWith("/tests") || testDir.endsWith("/__tests__")) {
|
|
6314
|
-
const parentDir =
|
|
6843
|
+
const parentDir = dirname13(testDir);
|
|
6315
6844
|
possiblePaths.push(parentDir + "/" + sourceFileName);
|
|
6316
6845
|
}
|
|
6317
6846
|
if (testDir.includes("test")) {
|
|
@@ -6508,7 +7037,7 @@ function generateTestStatistics(graph) {
|
|
|
6508
7037
|
`;
|
|
6509
7038
|
const dirTestCoverage = /* @__PURE__ */ new Map();
|
|
6510
7039
|
for (const sourceFile of sourceFiles) {
|
|
6511
|
-
const dir =
|
|
7040
|
+
const dir = dirname13(sourceFile).split("/")[0];
|
|
6512
7041
|
if (!dirTestCoverage.has(dir)) {
|
|
6513
7042
|
dirTestCoverage.set(dir, { total: 0, tested: 0 });
|
|
6514
7043
|
}
|
|
@@ -6531,7 +7060,7 @@ function generateTestStatistics(graph) {
|
|
|
6531
7060
|
}
|
|
6532
7061
|
|
|
6533
7062
|
// src/docs/history.ts
|
|
6534
|
-
import { dirname as
|
|
7063
|
+
import { dirname as dirname14 } from "path";
|
|
6535
7064
|
import { execSync } from "child_process";
|
|
6536
7065
|
function generateHistory(graph, projectRoot, version) {
|
|
6537
7066
|
let output = "";
|
|
@@ -6812,7 +7341,7 @@ function generateFeatureClusters(graph) {
|
|
|
6812
7341
|
const dirFiles = /* @__PURE__ */ new Map();
|
|
6813
7342
|
const fileEdges = /* @__PURE__ */ new Map();
|
|
6814
7343
|
graph.forEachNode((node, attrs) => {
|
|
6815
|
-
const dir =
|
|
7344
|
+
const dir = dirname14(attrs.filePath);
|
|
6816
7345
|
if (!dirFiles.has(dir)) {
|
|
6817
7346
|
dirFiles.set(dir, /* @__PURE__ */ new Set());
|
|
6818
7347
|
}
|
|
@@ -6894,7 +7423,7 @@ function capitalizeFirst3(str) {
|
|
|
6894
7423
|
}
|
|
6895
7424
|
|
|
6896
7425
|
// src/docs/current.ts
|
|
6897
|
-
import { dirname as
|
|
7426
|
+
import { dirname as dirname15 } from "path";
|
|
6898
7427
|
function generateCurrent(graph, projectRoot, version) {
|
|
6899
7428
|
let output = "";
|
|
6900
7429
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -7032,7 +7561,7 @@ function generateCompleteFileIndex(graph) {
|
|
|
7032
7561
|
fileInfos.sort((a, b) => a.filePath.localeCompare(b.filePath));
|
|
7033
7562
|
const dirGroups = /* @__PURE__ */ new Map();
|
|
7034
7563
|
for (const info of fileInfos) {
|
|
7035
|
-
const dir =
|
|
7564
|
+
const dir = dirname15(info.filePath);
|
|
7036
7565
|
const topDir = dir === "." ? "root" : dir.split("/")[0];
|
|
7037
7566
|
if (!dirGroups.has(topDir)) {
|
|
7038
7567
|
dirGroups.set(topDir, []);
|
|
@@ -7243,8 +7772,8 @@ function getTopLevelDir2(filePath) {
|
|
|
7243
7772
|
}
|
|
7244
7773
|
|
|
7245
7774
|
// src/docs/status.ts
|
|
7246
|
-
import { readFileSync as
|
|
7247
|
-
import { join as
|
|
7775
|
+
import { readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
|
|
7776
|
+
import { join as join12 } from "path";
|
|
7248
7777
|
function generateStatus(graph, projectRoot, version) {
|
|
7249
7778
|
let output = "";
|
|
7250
7779
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -7277,12 +7806,12 @@ function getFileCount11(graph) {
|
|
|
7277
7806
|
}
|
|
7278
7807
|
function extractComments(projectRoot, filePath) {
|
|
7279
7808
|
const comments = [];
|
|
7280
|
-
const fullPath =
|
|
7281
|
-
if (!
|
|
7809
|
+
const fullPath = join12(projectRoot, filePath);
|
|
7810
|
+
if (!existsSync10(fullPath)) {
|
|
7282
7811
|
return comments;
|
|
7283
7812
|
}
|
|
7284
7813
|
try {
|
|
7285
|
-
const content =
|
|
7814
|
+
const content = readFileSync8(fullPath, "utf-8");
|
|
7286
7815
|
const lines = content.split("\n");
|
|
7287
7816
|
const patterns = [
|
|
7288
7817
|
{ type: "TODO", regex: /(?:\/\/|#|\/\*)\s*TODO:?\s*(.+)/i },
|
|
@@ -7844,15 +8373,15 @@ function generateConfidenceSection(title, description, symbols, projectRoot) {
|
|
|
7844
8373
|
}
|
|
7845
8374
|
|
|
7846
8375
|
// src/docs/metadata.ts
|
|
7847
|
-
import { existsSync as
|
|
7848
|
-
import { join as
|
|
8376
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9, writeFileSync as writeFileSync2 } from "fs";
|
|
8377
|
+
import { join as join13 } from "path";
|
|
7849
8378
|
function loadMetadata(outputDir) {
|
|
7850
|
-
const metadataPath =
|
|
7851
|
-
if (!
|
|
8379
|
+
const metadataPath = join13(outputDir, "metadata.json");
|
|
8380
|
+
if (!existsSync11(metadataPath)) {
|
|
7852
8381
|
return null;
|
|
7853
8382
|
}
|
|
7854
8383
|
try {
|
|
7855
|
-
const content =
|
|
8384
|
+
const content = readFileSync9(metadataPath, "utf-8");
|
|
7856
8385
|
return JSON.parse(content);
|
|
7857
8386
|
} catch (err) {
|
|
7858
8387
|
console.error("Failed to load metadata:", err);
|
|
@@ -7860,7 +8389,7 @@ function loadMetadata(outputDir) {
|
|
|
7860
8389
|
}
|
|
7861
8390
|
}
|
|
7862
8391
|
function saveMetadata(outputDir, metadata) {
|
|
7863
|
-
const metadataPath =
|
|
8392
|
+
const metadataPath = join13(outputDir, "metadata.json");
|
|
7864
8393
|
writeFileSync2(metadataPath, JSON.stringify(metadata, null, 2), "utf-8");
|
|
7865
8394
|
}
|
|
7866
8395
|
function createMetadata(version, projectPath, fileCount, symbolCount, edgeCount, docTypes) {
|
|
@@ -7903,7 +8432,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
7903
8432
|
const generated = [];
|
|
7904
8433
|
const errors = [];
|
|
7905
8434
|
try {
|
|
7906
|
-
if (!
|
|
8435
|
+
if (!existsSync12(options.outputDir)) {
|
|
7907
8436
|
mkdirSync2(options.outputDir, { recursive: true });
|
|
7908
8437
|
if (options.verbose) {
|
|
7909
8438
|
console.log(`Created output directory: ${options.outputDir}`);
|
|
@@ -7942,7 +8471,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
7942
8471
|
try {
|
|
7943
8472
|
if (options.verbose) console.log("Generating ARCHITECTURE.md...");
|
|
7944
8473
|
const content = generateArchitecture(graph, projectRoot, version, parseTime);
|
|
7945
|
-
const filePath =
|
|
8474
|
+
const filePath = join14(options.outputDir, "ARCHITECTURE.md");
|
|
7946
8475
|
writeFileSync3(filePath, content, "utf-8");
|
|
7947
8476
|
generated.push("ARCHITECTURE.md");
|
|
7948
8477
|
} catch (err) {
|
|
@@ -7953,7 +8482,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
7953
8482
|
try {
|
|
7954
8483
|
if (options.verbose) console.log("Generating CONVENTIONS.md...");
|
|
7955
8484
|
const content = generateConventions(graph, projectRoot, version);
|
|
7956
|
-
const filePath =
|
|
8485
|
+
const filePath = join14(options.outputDir, "CONVENTIONS.md");
|
|
7957
8486
|
writeFileSync3(filePath, content, "utf-8");
|
|
7958
8487
|
generated.push("CONVENTIONS.md");
|
|
7959
8488
|
} catch (err) {
|
|
@@ -7964,7 +8493,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
7964
8493
|
try {
|
|
7965
8494
|
if (options.verbose) console.log("Generating DEPENDENCIES.md...");
|
|
7966
8495
|
const content = generateDependencies(graph, projectRoot, version);
|
|
7967
|
-
const filePath =
|
|
8496
|
+
const filePath = join14(options.outputDir, "DEPENDENCIES.md");
|
|
7968
8497
|
writeFileSync3(filePath, content, "utf-8");
|
|
7969
8498
|
generated.push("DEPENDENCIES.md");
|
|
7970
8499
|
} catch (err) {
|
|
@@ -7975,7 +8504,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
7975
8504
|
try {
|
|
7976
8505
|
if (options.verbose) console.log("Generating ONBOARDING.md...");
|
|
7977
8506
|
const content = generateOnboarding(graph, projectRoot, version);
|
|
7978
|
-
const filePath =
|
|
8507
|
+
const filePath = join14(options.outputDir, "ONBOARDING.md");
|
|
7979
8508
|
writeFileSync3(filePath, content, "utf-8");
|
|
7980
8509
|
generated.push("ONBOARDING.md");
|
|
7981
8510
|
} catch (err) {
|
|
@@ -7986,7 +8515,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
7986
8515
|
try {
|
|
7987
8516
|
if (options.verbose) console.log("Generating FILES.md...");
|
|
7988
8517
|
const content = generateFiles(graph, projectRoot, version);
|
|
7989
|
-
const filePath =
|
|
8518
|
+
const filePath = join14(options.outputDir, "FILES.md");
|
|
7990
8519
|
writeFileSync3(filePath, content, "utf-8");
|
|
7991
8520
|
generated.push("FILES.md");
|
|
7992
8521
|
} catch (err) {
|
|
@@ -7997,7 +8526,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
7997
8526
|
try {
|
|
7998
8527
|
if (options.verbose) console.log("Generating API_SURFACE.md...");
|
|
7999
8528
|
const content = generateApiSurface(graph, projectRoot, version);
|
|
8000
|
-
const filePath =
|
|
8529
|
+
const filePath = join14(options.outputDir, "API_SURFACE.md");
|
|
8001
8530
|
writeFileSync3(filePath, content, "utf-8");
|
|
8002
8531
|
generated.push("API_SURFACE.md");
|
|
8003
8532
|
} catch (err) {
|
|
@@ -8008,7 +8537,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8008
8537
|
try {
|
|
8009
8538
|
if (options.verbose) console.log("Generating ERRORS.md...");
|
|
8010
8539
|
const content = generateErrors(graph, projectRoot, version);
|
|
8011
|
-
const filePath =
|
|
8540
|
+
const filePath = join14(options.outputDir, "ERRORS.md");
|
|
8012
8541
|
writeFileSync3(filePath, content, "utf-8");
|
|
8013
8542
|
generated.push("ERRORS.md");
|
|
8014
8543
|
} catch (err) {
|
|
@@ -8019,7 +8548,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8019
8548
|
try {
|
|
8020
8549
|
if (options.verbose) console.log("Generating TESTS.md...");
|
|
8021
8550
|
const content = generateTests(graph, projectRoot, version);
|
|
8022
|
-
const filePath =
|
|
8551
|
+
const filePath = join14(options.outputDir, "TESTS.md");
|
|
8023
8552
|
writeFileSync3(filePath, content, "utf-8");
|
|
8024
8553
|
generated.push("TESTS.md");
|
|
8025
8554
|
} catch (err) {
|
|
@@ -8030,7 +8559,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8030
8559
|
try {
|
|
8031
8560
|
if (options.verbose) console.log("Generating HISTORY.md...");
|
|
8032
8561
|
const content = generateHistory(graph, projectRoot, version);
|
|
8033
|
-
const filePath =
|
|
8562
|
+
const filePath = join14(options.outputDir, "HISTORY.md");
|
|
8034
8563
|
writeFileSync3(filePath, content, "utf-8");
|
|
8035
8564
|
generated.push("HISTORY.md");
|
|
8036
8565
|
} catch (err) {
|
|
@@ -8041,7 +8570,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8041
8570
|
try {
|
|
8042
8571
|
if (options.verbose) console.log("Generating CURRENT.md...");
|
|
8043
8572
|
const content = generateCurrent(graph, projectRoot, version);
|
|
8044
|
-
const filePath =
|
|
8573
|
+
const filePath = join14(options.outputDir, "CURRENT.md");
|
|
8045
8574
|
writeFileSync3(filePath, content, "utf-8");
|
|
8046
8575
|
generated.push("CURRENT.md");
|
|
8047
8576
|
} catch (err) {
|
|
@@ -8052,7 +8581,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8052
8581
|
try {
|
|
8053
8582
|
if (options.verbose) console.log("Generating STATUS.md...");
|
|
8054
8583
|
const content = generateStatus(graph, projectRoot, version);
|
|
8055
|
-
const filePath =
|
|
8584
|
+
const filePath = join14(options.outputDir, "STATUS.md");
|
|
8056
8585
|
writeFileSync3(filePath, content, "utf-8");
|
|
8057
8586
|
generated.push("STATUS.md");
|
|
8058
8587
|
} catch (err) {
|
|
@@ -8063,7 +8592,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8063
8592
|
try {
|
|
8064
8593
|
if (options.verbose) console.log("Generating HEALTH.md...");
|
|
8065
8594
|
const content = generateHealth(graph, projectRoot, version);
|
|
8066
|
-
const filePath =
|
|
8595
|
+
const filePath = join14(options.outputDir, "HEALTH.md");
|
|
8067
8596
|
writeFileSync3(filePath, content, "utf-8");
|
|
8068
8597
|
generated.push("HEALTH.md");
|
|
8069
8598
|
} catch (err) {
|
|
@@ -8074,7 +8603,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8074
8603
|
try {
|
|
8075
8604
|
if (options.verbose) console.log("Generating DEAD_CODE.md...");
|
|
8076
8605
|
const content = generateDeadCode(graph, projectRoot, version);
|
|
8077
|
-
const filePath =
|
|
8606
|
+
const filePath = join14(options.outputDir, "DEAD_CODE.md");
|
|
8078
8607
|
writeFileSync3(filePath, content, "utf-8");
|
|
8079
8608
|
generated.push("DEAD_CODE.md");
|
|
8080
8609
|
} catch (err) {
|
|
@@ -8122,13 +8651,13 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
|
8122
8651
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8123
8652
|
|
|
8124
8653
|
// src/mcp/tools.ts
|
|
8125
|
-
import { dirname as
|
|
8126
|
-
import { existsSync as
|
|
8654
|
+
import { dirname as dirname16, join as join17 } from "path";
|
|
8655
|
+
import { existsSync as existsSync15, readFileSync as readFileSync11 } from "fs";
|
|
8127
8656
|
|
|
8128
8657
|
// src/mcp/connect.ts
|
|
8129
8658
|
import simpleGit from "simple-git";
|
|
8130
|
-
import { existsSync as
|
|
8131
|
-
import { join as
|
|
8659
|
+
import { existsSync as existsSync13 } from "fs";
|
|
8660
|
+
import { join as join15, basename as basename5, resolve as resolve2 } from "path";
|
|
8132
8661
|
import { tmpdir, homedir } from "os";
|
|
8133
8662
|
function validateProjectPath(source) {
|
|
8134
8663
|
const resolved = resolve2(source);
|
|
@@ -8141,11 +8670,11 @@ function validateProjectPath(source) {
|
|
|
8141
8670
|
"/boot",
|
|
8142
8671
|
"/proc",
|
|
8143
8672
|
"/sys",
|
|
8144
|
-
|
|
8145
|
-
|
|
8146
|
-
|
|
8147
|
-
|
|
8148
|
-
|
|
8673
|
+
join15(homedir(), ".ssh"),
|
|
8674
|
+
join15(homedir(), ".gnupg"),
|
|
8675
|
+
join15(homedir(), ".aws"),
|
|
8676
|
+
join15(homedir(), ".config"),
|
|
8677
|
+
join15(homedir(), ".env")
|
|
8149
8678
|
];
|
|
8150
8679
|
for (const blocked of blockedPaths) {
|
|
8151
8680
|
if (resolved.startsWith(blocked)) {
|
|
@@ -8168,11 +8697,11 @@ async function connectToRepo(source, subdirectory, state) {
|
|
|
8168
8697
|
};
|
|
8169
8698
|
}
|
|
8170
8699
|
projectName = match[1];
|
|
8171
|
-
const reposDir =
|
|
8172
|
-
const cloneDir =
|
|
8700
|
+
const reposDir = join15(tmpdir(), "depwire-repos");
|
|
8701
|
+
const cloneDir = join15(reposDir, projectName);
|
|
8173
8702
|
console.error(`Connecting to GitHub repo: ${source}`);
|
|
8174
8703
|
const git = simpleGit();
|
|
8175
|
-
if (
|
|
8704
|
+
if (existsSync13(cloneDir)) {
|
|
8176
8705
|
console.error(`Repo already cloned at ${cloneDir}, pulling latest changes...`);
|
|
8177
8706
|
try {
|
|
8178
8707
|
await git.cwd(cloneDir).pull();
|
|
@@ -8190,7 +8719,7 @@ async function connectToRepo(source, subdirectory, state) {
|
|
|
8190
8719
|
};
|
|
8191
8720
|
}
|
|
8192
8721
|
}
|
|
8193
|
-
projectRoot = subdirectory ?
|
|
8722
|
+
projectRoot = subdirectory ? join15(cloneDir, subdirectory) : cloneDir;
|
|
8194
8723
|
} else {
|
|
8195
8724
|
const validation2 = validateProjectPath(source);
|
|
8196
8725
|
if (!validation2.valid) {
|
|
@@ -8199,13 +8728,13 @@ async function connectToRepo(source, subdirectory, state) {
|
|
|
8199
8728
|
message: validation2.error
|
|
8200
8729
|
};
|
|
8201
8730
|
}
|
|
8202
|
-
if (!
|
|
8731
|
+
if (!existsSync13(source)) {
|
|
8203
8732
|
return {
|
|
8204
8733
|
error: "Directory not found",
|
|
8205
8734
|
message: `Directory does not exist: ${source}`
|
|
8206
8735
|
};
|
|
8207
8736
|
}
|
|
8208
|
-
projectRoot = subdirectory ?
|
|
8737
|
+
projectRoot = subdirectory ? join15(source, subdirectory) : source;
|
|
8209
8738
|
projectName = basename5(projectRoot);
|
|
8210
8739
|
}
|
|
8211
8740
|
const validation = validateProjectPath(projectRoot);
|
|
@@ -8215,7 +8744,7 @@ async function connectToRepo(source, subdirectory, state) {
|
|
|
8215
8744
|
message: validation.error
|
|
8216
8745
|
};
|
|
8217
8746
|
}
|
|
8218
|
-
if (!
|
|
8747
|
+
if (!existsSync13(projectRoot)) {
|
|
8219
8748
|
return {
|
|
8220
8749
|
error: "Project root not found",
|
|
8221
8750
|
message: `Directory does not exist: ${projectRoot}`
|
|
@@ -8496,24 +9025,24 @@ function getWeekNumber(date) {
|
|
|
8496
9025
|
}
|
|
8497
9026
|
|
|
8498
9027
|
// src/temporal/snapshots.ts
|
|
8499
|
-
import { writeFileSync as writeFileSync4, readFileSync as
|
|
8500
|
-
import { join as
|
|
9028
|
+
import { writeFileSync as writeFileSync4, readFileSync as readFileSync10, mkdirSync as mkdirSync3, existsSync as existsSync14, readdirSync as readdirSync5 } from "fs";
|
|
9029
|
+
import { join as join16 } from "path";
|
|
8501
9030
|
function saveSnapshot(snapshot, outputDir) {
|
|
8502
|
-
if (!
|
|
9031
|
+
if (!existsSync14(outputDir)) {
|
|
8503
9032
|
mkdirSync3(outputDir, { recursive: true });
|
|
8504
9033
|
}
|
|
8505
9034
|
const filename = `${snapshot.commitHash.substring(0, 8)}.json`;
|
|
8506
|
-
const filepath =
|
|
9035
|
+
const filepath = join16(outputDir, filename);
|
|
8507
9036
|
writeFileSync4(filepath, JSON.stringify(snapshot, null, 2), "utf-8");
|
|
8508
9037
|
}
|
|
8509
9038
|
function loadSnapshot(commitHash, outputDir) {
|
|
8510
9039
|
const shortHash = commitHash.substring(0, 8);
|
|
8511
|
-
const filepath =
|
|
8512
|
-
if (!
|
|
9040
|
+
const filepath = join16(outputDir, `${shortHash}.json`);
|
|
9041
|
+
if (!existsSync14(filepath)) {
|
|
8513
9042
|
return null;
|
|
8514
9043
|
}
|
|
8515
9044
|
try {
|
|
8516
|
-
const content =
|
|
9045
|
+
const content = readFileSync10(filepath, "utf-8");
|
|
8517
9046
|
return JSON.parse(content);
|
|
8518
9047
|
} catch {
|
|
8519
9048
|
return null;
|
|
@@ -9200,7 +9729,7 @@ function handleGetArchitectureSummary(graph) {
|
|
|
9200
9729
|
const dirMap = /* @__PURE__ */ new Map();
|
|
9201
9730
|
const languageBreakdown = {};
|
|
9202
9731
|
fileSummary.forEach((f) => {
|
|
9203
|
-
const dir = f.filePath.includes("/") ?
|
|
9732
|
+
const dir = f.filePath.includes("/") ? dirname16(f.filePath) : ".";
|
|
9204
9733
|
if (!dirMap.has(dir)) {
|
|
9205
9734
|
dirMap.set(dir, { fileCount: 0, symbolCount: 0 });
|
|
9206
9735
|
}
|
|
@@ -9287,8 +9816,8 @@ The server will keep running until you end the MCP session or press Ctrl+C.`;
|
|
|
9287
9816
|
};
|
|
9288
9817
|
}
|
|
9289
9818
|
async function handleGetProjectDocs(docType, state) {
|
|
9290
|
-
const docsDir =
|
|
9291
|
-
if (!
|
|
9819
|
+
const docsDir = join17(state.projectRoot, ".depwire");
|
|
9820
|
+
if (!existsSync15(docsDir)) {
|
|
9292
9821
|
const errorMessage = `Project documentation has not been generated yet.
|
|
9293
9822
|
|
|
9294
9823
|
Run \`depwire docs ${state.projectRoot}\` to generate codebase documentation.
|
|
@@ -9318,12 +9847,12 @@ Available document types:
|
|
|
9318
9847
|
missing.push(doc);
|
|
9319
9848
|
continue;
|
|
9320
9849
|
}
|
|
9321
|
-
const filePath =
|
|
9322
|
-
if (!
|
|
9850
|
+
const filePath = join17(docsDir, metadata.documents[doc].file);
|
|
9851
|
+
if (!existsSync15(filePath)) {
|
|
9323
9852
|
missing.push(doc);
|
|
9324
9853
|
continue;
|
|
9325
9854
|
}
|
|
9326
|
-
const content =
|
|
9855
|
+
const content = readFileSync11(filePath, "utf-8");
|
|
9327
9856
|
if (docsToReturn.length > 1) {
|
|
9328
9857
|
output += `
|
|
9329
9858
|
|
|
@@ -9348,16 +9877,16 @@ Available document types:
|
|
|
9348
9877
|
}
|
|
9349
9878
|
async function handleUpdateProjectDocs(docType, state) {
|
|
9350
9879
|
const startTime = Date.now();
|
|
9351
|
-
const docsDir =
|
|
9880
|
+
const docsDir = join17(state.projectRoot, ".depwire");
|
|
9352
9881
|
console.error("Regenerating project documentation...");
|
|
9353
9882
|
const parsedFiles = await parseProject(state.projectRoot);
|
|
9354
9883
|
const graph = buildGraph(parsedFiles);
|
|
9355
9884
|
const parseTime = (Date.now() - startTime) / 1e3;
|
|
9356
9885
|
state.graph = graph;
|
|
9357
|
-
const packageJsonPath =
|
|
9358
|
-
const packageJson = JSON.parse(
|
|
9886
|
+
const packageJsonPath = join17(__dirname, "../../package.json");
|
|
9887
|
+
const packageJson = JSON.parse(readFileSync11(packageJsonPath, "utf-8"));
|
|
9359
9888
|
const docsToGenerate = docType === "all" ? ["architecture", "conventions", "dependencies", "onboarding"] : [docType];
|
|
9360
|
-
const docsExist =
|
|
9889
|
+
const docsExist = existsSync15(docsDir);
|
|
9361
9890
|
const result = await generateDocs(graph, state.projectRoot, packageJson.version, parseTime, {
|
|
9362
9891
|
outputDir: docsDir,
|
|
9363
9892
|
format: "markdown",
|
|
@@ -9416,7 +9945,7 @@ async function handleGetTemporalGraph(state, commits, strategy) {
|
|
|
9416
9945
|
}
|
|
9417
9946
|
const sampledCommits = sampleCommits(allCommits, commits, strategy);
|
|
9418
9947
|
const snapshots = [];
|
|
9419
|
-
const outputDir =
|
|
9948
|
+
const outputDir = join17(projectRoot, ".depwire", "temporal");
|
|
9420
9949
|
for (const commit of sampledCommits) {
|
|
9421
9950
|
const existing = loadSnapshot(commit.hash, outputDir);
|
|
9422
9951
|
if (existing) {
|