depwire-cli 0.9.27 → 0.9.29

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.
@@ -1,5 +1,5 @@
1
1
  // src/utils/files.ts
2
- import { readdirSync, statSync, existsSync, lstatSync } from "fs";
2
+ import { readdirSync, statSync, existsSync, lstatSync, realpathSync } from "fs";
3
3
  import { join, relative } from "path";
4
4
  import os from "os";
5
5
  function scanDirectory(rootDir, baseDir = rootDir) {
@@ -50,6 +50,7 @@ function fileExists(filePath) {
50
50
  }
51
51
  }
52
52
  function findProjectRoot(startDir = process.cwd()) {
53
+ startDir = realpathSync(startDir);
53
54
  const projectMarkers = [
54
55
  "package.json",
55
56
  // Node.js
@@ -79,9 +80,6 @@ function findProjectRoot(startDir = process.cwd()) {
79
80
  let depth = 0;
80
81
  const home = os.homedir();
81
82
  while (currentDir !== rootDir && depth < maxDepth) {
82
- if (currentDir === home || !currentDir.startsWith(home)) {
83
- break;
84
- }
85
83
  const dirName = currentDir.split("/").pop();
86
84
  if (dirName && blocklist.includes(dirName)) {
87
85
  console.warn(`\u26A0\uFE0F Skipping blocked directory: ${dirName}`);
@@ -2873,9 +2871,449 @@ async function parseProject(projectRoot, options) {
2873
2871
  return parsedFiles;
2874
2872
  }
2875
2873
 
2874
+ // src/cross-language/detectors/rest-api.ts
2875
+ import { readFileSync as readFileSync6 } from "fs";
2876
+ import { join as join9, resolve as resolve4 } from "path";
2877
+ function getLanguage(filePath) {
2878
+ if (filePath.endsWith(".ts") || filePath.endsWith(".tsx")) return "typescript";
2879
+ if (filePath.endsWith(".js") || filePath.endsWith(".jsx") || filePath.endsWith(".mjs") || filePath.endsWith(".cjs")) return "javascript";
2880
+ if (filePath.endsWith(".py")) return "python";
2881
+ if (filePath.endsWith(".go")) return "go";
2882
+ return "unknown";
2883
+ }
2884
+ function normalizePath(routePath) {
2885
+ return routePath.replace(/:[a-zA-Z_][a-zA-Z0-9_]*/g, "__PARAM__").replace(/\{[a-zA-Z_][a-zA-Z0-9_]*\}/g, "__PARAM__");
2886
+ }
2887
+ function stripTrailingSlash(p) {
2888
+ return p.length > 1 && p.endsWith("/") ? p.slice(0, -1) : p;
2889
+ }
2890
+ function extractHttpCalls(source, filePath) {
2891
+ const calls = [];
2892
+ const lines = source.split("\n");
2893
+ for (let i = 0; i < lines.length; i++) {
2894
+ const line = lines[i];
2895
+ const fetchMatch = line.match(/fetch\s*\(\s*(['"`])([^'"`]+)\1/);
2896
+ if (fetchMatch) {
2897
+ const path6 = fetchMatch[2];
2898
+ if (isLocalApiPath(path6)) {
2899
+ const methodMatch = line.match(/method\s*:\s*['"](\w+)['"]/);
2900
+ const method = methodMatch ? methodMatch[1].toUpperCase() : "GET";
2901
+ calls.push({ method, path: cleanPath(path6), file: filePath, line: i + 1 });
2902
+ }
2903
+ }
2904
+ if (!fetchMatch) {
2905
+ const fetchTemplateMatch = line.match(/fetch\s*\(\s*`([^`]+)`/);
2906
+ if (fetchTemplateMatch) {
2907
+ const path6 = fetchTemplateMatch[1];
2908
+ if (isLocalApiPath(path6)) {
2909
+ const methodMatch = line.match(/method\s*:\s*['"](\w+)['"]/);
2910
+ const method = methodMatch ? methodMatch[1].toUpperCase() : "GET";
2911
+ calls.push({ method, path: cleanPath(path6), file: filePath, line: i + 1 });
2912
+ }
2913
+ }
2914
+ }
2915
+ const axiosMatch = line.match(/axios\s*\.\s*(get|post|put|delete|patch)\s*\(\s*(['"`])([^'"`]+)\2/i);
2916
+ if (axiosMatch) {
2917
+ const path6 = axiosMatch[3];
2918
+ if (isLocalApiPath(path6)) {
2919
+ calls.push({ method: axiosMatch[1].toUpperCase(), path: cleanPath(path6), file: filePath, line: i + 1 });
2920
+ }
2921
+ }
2922
+ if (!axiosMatch) {
2923
+ const axiosTemplateMatch = line.match(/axios\s*\.\s*(get|post|put|delete|patch)\s*\(\s*`([^`]+)`/i);
2924
+ if (axiosTemplateMatch) {
2925
+ const path6 = axiosTemplateMatch[2];
2926
+ if (isLocalApiPath(path6)) {
2927
+ calls.push({ method: axiosTemplateMatch[1].toUpperCase(), path: cleanPath(path6), file: filePath, line: i + 1 });
2928
+ }
2929
+ }
2930
+ }
2931
+ const genericMatch = line.match(/\w+\s*\.\s*(get|post|put|delete|patch)\s*\(\s*(['"`])([^'"`]+)\2/i);
2932
+ if (genericMatch && !line.match(/axios/) && !line.match(/app\s*\./) && !line.match(/router\s*\./) && !line.match(/r\s*\./)) {
2933
+ const path6 = genericMatch[3];
2934
+ if (isLocalApiPath(path6)) {
2935
+ calls.push({ method: genericMatch[1].toUpperCase(), path: cleanPath(path6), file: filePath, line: i + 1 });
2936
+ }
2937
+ }
2938
+ }
2939
+ return calls;
2940
+ }
2941
+ function isLocalApiPath(path6) {
2942
+ if (path6.startsWith("http://") || path6.startsWith("https://")) return false;
2943
+ return path6.startsWith("/") || path6.includes("/api/");
2944
+ }
2945
+ function cleanPath(path6) {
2946
+ let cleaned = path6.replace(/\$\{[^}]*\}/g, "");
2947
+ cleaned = stripTrailingSlash(cleaned);
2948
+ return cleaned;
2949
+ }
2950
+ function extractRouteDefinitions(source, filePath) {
2951
+ const routes = [];
2952
+ const lines = source.split("\n");
2953
+ const lang = getLanguage(filePath);
2954
+ for (let i = 0; i < lines.length; i++) {
2955
+ const line = lines[i];
2956
+ if (lang === "typescript" || lang === "javascript") {
2957
+ const expressMatch = line.match(/(?:app|router)\s*\.\s*(get|post|put|delete|patch)\s*\(\s*(['"`])([^'"`]+)\2/i);
2958
+ if (expressMatch) {
2959
+ const path6 = expressMatch[3];
2960
+ if (path6.startsWith("/")) {
2961
+ routes.push({
2962
+ method: expressMatch[1].toUpperCase(),
2963
+ path: path6,
2964
+ normalizedPath: normalizePath(path6),
2965
+ file: filePath,
2966
+ line: i + 1
2967
+ });
2968
+ }
2969
+ }
2970
+ }
2971
+ if (lang === "python") {
2972
+ const pythonMatch = line.match(/@(?:app|router)\s*\.\s*(get|post|put|delete|patch)\s*\(\s*(['"])([^'"]+)\2/i);
2973
+ if (pythonMatch) {
2974
+ const path6 = pythonMatch[3];
2975
+ if (path6.startsWith("/")) {
2976
+ routes.push({
2977
+ method: pythonMatch[1].toUpperCase(),
2978
+ path: path6,
2979
+ normalizedPath: normalizePath(path6),
2980
+ file: filePath,
2981
+ line: i + 1
2982
+ });
2983
+ }
2984
+ }
2985
+ const flaskMatch = line.match(/@(?:app|blueprint|router)\s*\.\s*route\s*\(\s*(['"])([^'"]+)\1/);
2986
+ if (flaskMatch) {
2987
+ const path6 = flaskMatch[2];
2988
+ if (path6.startsWith("/")) {
2989
+ const methodsMatch = line.match(/methods\s*=\s*\[([^\]]+)\]/);
2990
+ const methods = methodsMatch ? methodsMatch[1].match(/['"](\w+)['"]/g)?.map((m) => m.replace(/['"]/g, "").toUpperCase()) || ["GET"] : ["GET"];
2991
+ for (const method of methods) {
2992
+ routes.push({
2993
+ method,
2994
+ path: path6,
2995
+ normalizedPath: normalizePath(path6),
2996
+ file: filePath,
2997
+ line: i + 1
2998
+ });
2999
+ }
3000
+ }
3001
+ }
3002
+ }
3003
+ if (lang === "go") {
3004
+ const goMatch = line.match(/(?:r|router|group)\s*\.\s*(GET|POST|PUT|DELETE|PATCH)\s*\(\s*"([^"]+)"/);
3005
+ if (goMatch) {
3006
+ const path6 = goMatch[2];
3007
+ if (path6.startsWith("/")) {
3008
+ routes.push({
3009
+ method: goMatch[1].toUpperCase(),
3010
+ path: path6,
3011
+ normalizedPath: normalizePath(path6),
3012
+ file: filePath,
3013
+ line: i + 1
3014
+ });
3015
+ }
3016
+ }
3017
+ }
3018
+ }
3019
+ return routes;
3020
+ }
3021
+ function matchPaths(callPath, routeNormalized) {
3022
+ const normalizedCall = normalizePath(stripTrailingSlash(callPath));
3023
+ const normalizedRoute = stripTrailingSlash(routeNormalized);
3024
+ if (normalizedCall === normalizedRoute) return true;
3025
+ if (normalizedRoute.startsWith(normalizedCall) && normalizedRoute[normalizedCall.length] === "/") return true;
3026
+ const callParts = normalizedCall.split("/");
3027
+ const routeParts = normalizedRoute.split("/");
3028
+ if (callParts.length <= routeParts.length) {
3029
+ let match = true;
3030
+ for (let i = 0; i < callParts.length; i++) {
3031
+ if (routeParts[i] === "__PARAM__") continue;
3032
+ if (callParts[i] !== routeParts[i]) {
3033
+ match = false;
3034
+ break;
3035
+ }
3036
+ }
3037
+ if (match) return true;
3038
+ }
3039
+ return false;
3040
+ }
3041
+ function getConfidence(callPath, callMethod, routePath, routeMethod) {
3042
+ const normalizedCall = normalizePath(stripTrailingSlash(callPath));
3043
+ const normalizedRoute = normalizePath(stripTrailingSlash(routePath));
3044
+ const exactPath = normalizedCall === normalizedRoute;
3045
+ const methodMatch = callMethod === routeMethod;
3046
+ if (exactPath && methodMatch) return "high";
3047
+ if (exactPath) return "medium";
3048
+ if (methodMatch) return "medium";
3049
+ return "low";
3050
+ }
3051
+ function detectRestApiEdges(files, projectRoot) {
3052
+ const edges = [];
3053
+ const allCalls = [];
3054
+ const allRoutes = [];
3055
+ for (const file of files) {
3056
+ const fullPath = join9(projectRoot, file.filePath);
3057
+ if (!resolve4(fullPath).startsWith(resolve4(projectRoot))) continue;
3058
+ let source;
3059
+ try {
3060
+ source = readFileSync6(fullPath, "utf-8");
3061
+ } catch {
3062
+ continue;
3063
+ }
3064
+ const lang = getLanguage(file.filePath);
3065
+ if (lang === "typescript" || lang === "javascript") {
3066
+ allCalls.push(...extractHttpCalls(source, file.filePath));
3067
+ }
3068
+ allRoutes.push(...extractRouteDefinitions(source, file.filePath));
3069
+ }
3070
+ for (const call of allCalls) {
3071
+ for (const route of allRoutes) {
3072
+ if (call.file === route.file) continue;
3073
+ if (matchPaths(call.path, route.normalizedPath)) {
3074
+ const confidence = getConfidence(call.path, call.method, route.path, route.method);
3075
+ edges.push({
3076
+ sourceFile: call.file,
3077
+ targetFile: route.file,
3078
+ edgeType: "rest-api",
3079
+ confidence,
3080
+ sourceLanguage: getLanguage(call.file),
3081
+ targetLanguage: getLanguage(route.file),
3082
+ sourceLine: call.line,
3083
+ targetLine: route.line,
3084
+ metadata: {
3085
+ httpMethod: call.method,
3086
+ path: call.path
3087
+ }
3088
+ });
3089
+ }
3090
+ }
3091
+ }
3092
+ return edges;
3093
+ }
3094
+
3095
+ // src/cross-language/detectors/subprocess.ts
3096
+ import { readFileSync as readFileSync7 } from "fs";
3097
+ import { join as join10, resolve as resolve5, basename } from "path";
3098
+ var SCRIPT_EXTENSIONS = [".py", ".js", ".ts", ".go", ".rs"];
3099
+ function getLanguage2(filePath) {
3100
+ if (filePath.endsWith(".ts") || filePath.endsWith(".tsx")) return "typescript";
3101
+ if (filePath.endsWith(".js") || filePath.endsWith(".jsx") || filePath.endsWith(".mjs") || filePath.endsWith(".cjs")) return "javascript";
3102
+ if (filePath.endsWith(".py")) return "python";
3103
+ if (filePath.endsWith(".go")) return "go";
3104
+ if (filePath.endsWith(".rs")) return "rust";
3105
+ return "unknown";
3106
+ }
3107
+ function extractFilenameFromArgs(args) {
3108
+ const tokens = args.split(/[\s,'"[\]]+/).filter(Boolean);
3109
+ for (const token of tokens) {
3110
+ for (const ext of SCRIPT_EXTENSIONS) {
3111
+ if (token.endsWith(ext)) {
3112
+ return token;
3113
+ }
3114
+ }
3115
+ }
3116
+ return null;
3117
+ }
3118
+ function extractSubprocessCalls(source, filePath) {
3119
+ const calls = [];
3120
+ const lines = source.split("\n");
3121
+ const lang = getLanguage2(filePath);
3122
+ for (let i = 0; i < lines.length; i++) {
3123
+ const line = lines[i];
3124
+ if (lang === "typescript" || lang === "javascript") {
3125
+ const execMatch = line.match(/(?:execSync|exec)\s*\(\s*(['"`])([^'"`]+)\1/);
3126
+ if (execMatch) {
3127
+ const command = execMatch[2];
3128
+ const calledFile = extractFilenameFromArgs(command);
3129
+ if (calledFile) {
3130
+ calls.push({ file: filePath, line: i + 1, command, calledFile });
3131
+ }
3132
+ }
3133
+ if (!execMatch) {
3134
+ const execTemplateMatch = line.match(/(?:execSync|exec)\s*\(\s*`([^`]+)`/);
3135
+ if (execTemplateMatch) {
3136
+ const command = execTemplateMatch[1].replace(/\$\{[^}]*\}/g, "");
3137
+ const calledFile = extractFilenameFromArgs(command);
3138
+ if (calledFile) {
3139
+ calls.push({ file: filePath, line: i + 1, command: execTemplateMatch[1], calledFile });
3140
+ }
3141
+ }
3142
+ }
3143
+ const spawnMatch = line.match(/(?:spawn|spawnSync)\s*\(\s*['"](\w+)['"]\s*,\s*\[([^\]]*)\]/);
3144
+ if (spawnMatch) {
3145
+ const command = `${spawnMatch[1]} ${spawnMatch[2]}`;
3146
+ const calledFile = extractFilenameFromArgs(spawnMatch[2]);
3147
+ if (calledFile) {
3148
+ calls.push({ file: filePath, line: i + 1, command, calledFile });
3149
+ }
3150
+ }
3151
+ }
3152
+ if (lang === "python") {
3153
+ const subprocessMatch = line.match(/subprocess\s*\.\s*(?:run|call|Popen|check_output|check_call)\s*\(\s*\[([^\]]*)\]/);
3154
+ if (subprocessMatch) {
3155
+ const command = subprocessMatch[1];
3156
+ const calledFile = extractFilenameFromArgs(command);
3157
+ if (calledFile) {
3158
+ calls.push({ file: filePath, line: i + 1, command, calledFile });
3159
+ }
3160
+ }
3161
+ const osMatch = line.match(/os\s*\.\s*system\s*\(\s*['"]([^'"]+)['"]/);
3162
+ if (osMatch) {
3163
+ const command = osMatch[1];
3164
+ const calledFile = extractFilenameFromArgs(command);
3165
+ if (calledFile) {
3166
+ calls.push({ file: filePath, line: i + 1, command, calledFile });
3167
+ }
3168
+ }
3169
+ const subprocessStrMatch = line.match(/subprocess\s*\.\s*(?:run|call|Popen|check_output|check_call)\s*\(\s*['"]([^'"]+)['"]/);
3170
+ if (subprocessStrMatch) {
3171
+ const command = subprocessStrMatch[1];
3172
+ const calledFile = extractFilenameFromArgs(command);
3173
+ if (calledFile) {
3174
+ calls.push({ file: filePath, line: i + 1, command, calledFile });
3175
+ }
3176
+ }
3177
+ }
3178
+ if (lang === "go") {
3179
+ const goMatch = line.match(/exec\s*\.\s*Command\s*\(\s*"([^"]+)"\s*,\s*"([^"]+)"/);
3180
+ if (goMatch) {
3181
+ const command = `${goMatch[1]} ${goMatch[2]}`;
3182
+ const calledFile = extractFilenameFromArgs(command);
3183
+ if (calledFile) {
3184
+ calls.push({ file: filePath, line: i + 1, command, calledFile });
3185
+ }
3186
+ }
3187
+ }
3188
+ }
3189
+ return calls;
3190
+ }
3191
+ function detectSubprocessEdges(files, projectRoot) {
3192
+ const edges = [];
3193
+ const knownFiles = new Set(files.map((f) => f.filePath));
3194
+ const basenameMap = /* @__PURE__ */ new Map();
3195
+ for (const f of files) {
3196
+ const base = basename(f.filePath);
3197
+ if (!basenameMap.has(base)) basenameMap.set(base, []);
3198
+ basenameMap.get(base).push(f.filePath);
3199
+ }
3200
+ for (const file of files) {
3201
+ const fullPath = join10(projectRoot, file.filePath);
3202
+ if (!resolve5(fullPath).startsWith(resolve5(projectRoot))) continue;
3203
+ let source;
3204
+ try {
3205
+ source = readFileSync7(fullPath, "utf-8");
3206
+ } catch {
3207
+ continue;
3208
+ }
3209
+ const calls = extractSubprocessCalls(source, file.filePath);
3210
+ for (const call of calls) {
3211
+ let targetFile = null;
3212
+ let confidence = "high";
3213
+ if (knownFiles.has(call.calledFile)) {
3214
+ targetFile = call.calledFile;
3215
+ confidence = "high";
3216
+ } else {
3217
+ const base = basename(call.calledFile);
3218
+ const candidates = basenameMap.get(base);
3219
+ if (candidates && candidates.length > 0) {
3220
+ const exactCandidate = candidates.find((c) => c.endsWith(call.calledFile));
3221
+ if (exactCandidate) {
3222
+ targetFile = exactCandidate;
3223
+ confidence = "high";
3224
+ } else {
3225
+ targetFile = candidates[0];
3226
+ confidence = "medium";
3227
+ }
3228
+ }
3229
+ }
3230
+ if (!targetFile) continue;
3231
+ if (targetFile === call.file) continue;
3232
+ edges.push({
3233
+ sourceFile: call.file,
3234
+ targetFile,
3235
+ edgeType: "subprocess",
3236
+ confidence,
3237
+ sourceLanguage: getLanguage2(call.file),
3238
+ targetLanguage: getLanguage2(targetFile),
3239
+ sourceLine: call.line,
3240
+ metadata: {
3241
+ command: call.command,
3242
+ calledFile: call.calledFile
3243
+ }
3244
+ });
3245
+ }
3246
+ }
3247
+ return edges;
3248
+ }
3249
+
3250
+ // src/cross-language/index.ts
3251
+ function detectCrossLanguageEdges(files, projectRoot, graph) {
3252
+ const startTime = Date.now();
3253
+ const restApiEdges = detectRestApiEdges(files, projectRoot);
3254
+ const subprocessEdges = detectSubprocessEdges(files, projectRoot);
3255
+ const allEdges = [...restApiEdges, ...subprocessEdges];
3256
+ for (const edge of allEdges) {
3257
+ const sourceNodeId = `${edge.sourceFile}::__file__`;
3258
+ const targetNodeId = `${edge.targetFile}::__file__`;
3259
+ if (!graph.hasNode(sourceNodeId)) {
3260
+ let hasSourceFile = false;
3261
+ graph.forEachNode((_nodeId, attrs) => {
3262
+ if (attrs.filePath === edge.sourceFile) hasSourceFile = true;
3263
+ });
3264
+ if (!hasSourceFile) continue;
3265
+ graph.addNode(sourceNodeId, {
3266
+ name: "__file__",
3267
+ kind: "import",
3268
+ filePath: edge.sourceFile,
3269
+ startLine: 1,
3270
+ endLine: 1,
3271
+ exported: false
3272
+ });
3273
+ }
3274
+ if (!graph.hasNode(targetNodeId)) {
3275
+ let hasTargetFile = false;
3276
+ graph.forEachNode((_nodeId, attrs) => {
3277
+ if (attrs.filePath === edge.targetFile) hasTargetFile = true;
3278
+ });
3279
+ if (!hasTargetFile) continue;
3280
+ graph.addNode(targetNodeId, {
3281
+ name: "__file__",
3282
+ kind: "import",
3283
+ filePath: edge.targetFile,
3284
+ startLine: 1,
3285
+ endLine: 1,
3286
+ exported: false
3287
+ });
3288
+ }
3289
+ graph.mergeEdge(sourceNodeId, targetNodeId, {
3290
+ kind: edge.edgeType,
3291
+ filePath: edge.sourceFile,
3292
+ line: edge.sourceLine || 1,
3293
+ crossLanguage: true,
3294
+ confidence: edge.confidence,
3295
+ edgeType: edge.edgeType,
3296
+ httpMethod: edge.metadata.httpMethod,
3297
+ path: edge.metadata.path,
3298
+ command: edge.metadata.command,
3299
+ calledFile: edge.metadata.calledFile
3300
+ });
3301
+ }
3302
+ const detectionTimeMs = Date.now() - startTime;
3303
+ return {
3304
+ edges: allEdges,
3305
+ stats: {
3306
+ restApiEdges: restApiEdges.length,
3307
+ subprocessEdges: subprocessEdges.length,
3308
+ filesAnalyzed: files.length,
3309
+ detectionTimeMs
3310
+ }
3311
+ };
3312
+ }
3313
+
2876
3314
  // src/graph/index.ts
2877
3315
  import { DirectedGraph } from "graphology";
2878
- function buildGraph(parsedFiles) {
3316
+ function buildGraph(parsedFiles, projectRoot) {
2879
3317
  const graph = new DirectedGraph();
2880
3318
  for (const file of parsedFiles) {
2881
3319
  for (const symbol of file.symbols) {
@@ -2932,6 +3370,12 @@ function buildGraph(parsedFiles) {
2932
3370
  }
2933
3371
  }
2934
3372
  }
3373
+ if (projectRoot) {
3374
+ const result = detectCrossLanguageEdges(parsedFiles, projectRoot, graph);
3375
+ if (result.stats.restApiEdges > 0 || result.stats.subprocessEdges > 0) {
3376
+ console.error(`Cross-language edges: ${result.stats.restApiEdges} rest-api, ${result.stats.subprocessEdges} subprocess detected`);
3377
+ }
3378
+ }
2935
3379
  return graph;
2936
3380
  }
2937
3381
 
@@ -3063,7 +3507,9 @@ function getCrossFileEdges(graph) {
3063
3507
  target,
3064
3508
  sourceFile: sourceAttrs.filePath,
3065
3509
  targetFile: targetAttrs.filePath,
3066
- kind: attrs.kind
3510
+ kind: attrs.kind,
3511
+ crossLanguage: attrs.crossLanguage || false,
3512
+ edgeType: attrs.edgeType
3067
3513
  });
3068
3514
  }
3069
3515
  });
@@ -3511,8 +3957,8 @@ function calculateDepthScore(graph) {
3511
3957
  }
3512
3958
 
3513
3959
  // src/health/index.ts
3514
- import { readFileSync as readFileSync6, writeFileSync, existsSync as existsSync8, mkdirSync } from "fs";
3515
- import { dirname as dirname8, resolve as resolve4 } from "path";
3960
+ import { readFileSync as readFileSync8, writeFileSync, existsSync as existsSync8, mkdirSync } from "fs";
3961
+ import { dirname as dirname8, resolve as resolve6 } from "path";
3516
3962
  function calculateHealthScore(graph, projectRoot) {
3517
3963
  const coupling = calculateCouplingScore(graph);
3518
3964
  const cohesion = calculateCohesionScore(graph);
@@ -3611,8 +4057,8 @@ function getHealthTrend(projectRoot, currentScore) {
3611
4057
  }
3612
4058
  }
3613
4059
  function saveHealthHistory(projectRoot, report) {
3614
- const resolvedRoot = resolve4(projectRoot);
3615
- const historyFile = resolve4(resolvedRoot, ".depwire", "health-history.json");
4060
+ const resolvedRoot = resolve6(projectRoot);
4061
+ const historyFile = resolve6(resolvedRoot, ".depwire", "health-history.json");
3616
4062
  if (!historyFile.startsWith(resolvedRoot)) {
3617
4063
  return;
3618
4064
  }
@@ -3630,7 +4076,7 @@ function saveHealthHistory(projectRoot, report) {
3630
4076
  if (existsSync8(historyFile)) {
3631
4077
  try {
3632
4078
  if (!historyFile.startsWith(resolvedRoot)) return;
3633
- const content = readFileSync6(historyFile, "utf-8");
4079
+ const content = readFileSync8(historyFile, "utf-8");
3634
4080
  history = JSON.parse(content);
3635
4081
  } catch {
3636
4082
  }
@@ -3644,14 +4090,14 @@ function saveHealthHistory(projectRoot, report) {
3644
4090
  writeFileSync(historyFile, JSON.stringify(history, null, 2), "utf-8");
3645
4091
  }
3646
4092
  function loadHealthHistory(projectRoot) {
3647
- const resolvedRoot = resolve4(projectRoot);
3648
- const historyFile = resolve4(resolvedRoot, ".depwire", "health-history.json");
4093
+ const resolvedRoot = resolve6(projectRoot);
4094
+ const historyFile = resolve6(resolvedRoot, ".depwire", "health-history.json");
3649
4095
  if (!historyFile.startsWith(resolvedRoot) || !existsSync8(historyFile)) {
3650
4096
  return [];
3651
4097
  }
3652
4098
  try {
3653
4099
  if (!historyFile.startsWith(resolvedRoot)) return [];
3654
- const content = readFileSync6(historyFile, "utf-8");
4100
+ const content = readFileSync8(historyFile, "utf-8");
3655
4101
  return JSON.parse(content);
3656
4102
  } catch {
3657
4103
  return [];
@@ -3660,7 +4106,7 @@ function loadHealthHistory(projectRoot) {
3660
4106
 
3661
4107
  // src/dead-code/detector.ts
3662
4108
  import path2 from "path";
3663
- import { readFileSync as readFileSync7, existsSync as existsSync9 } from "fs";
4109
+ import { readFileSync as readFileSync9, existsSync as existsSync9 } from "fs";
3664
4110
  function findDeadSymbols(graph, projectRoot, includeTests = false, debug = false) {
3665
4111
  const deadSymbols = [];
3666
4112
  const context = { graph, projectRoot };
@@ -3795,7 +4241,7 @@ function getPackageEntryPoints(projectRoot) {
3795
4241
  return entryPoints;
3796
4242
  }
3797
4243
  try {
3798
- const packageJson = JSON.parse(readFileSync7(packageJsonPath, "utf-8"));
4244
+ const packageJson = JSON.parse(readFileSync9(packageJsonPath, "utf-8"));
3799
4245
  if (packageJson.main) {
3800
4246
  entryPoints.add(path2.resolve(projectRoot, packageJson.main));
3801
4247
  }
@@ -3936,8 +4382,8 @@ function generateReason(symbol, confidence) {
3936
4382
  return "Potentially unused";
3937
4383
  }
3938
4384
  function isBarrelFile(filePath) {
3939
- const basename4 = path3.basename(filePath);
3940
- return basename4 === "index.ts" || basename4 === "index.js";
4385
+ const basename5 = path3.basename(filePath);
4386
+ return basename5 === "index.ts" || basename5 === "index.js";
3941
4387
  }
3942
4388
  function isTestFile2(filePath) {
3943
4389
  return filePath.includes("__tests__/") || filePath.includes(".test.") || filePath.includes(".spec.") || filePath.includes("/test/") || filePath.includes("/tests/");
@@ -4117,7 +4563,7 @@ function filterByConfidence(symbols, minConfidence) {
4117
4563
 
4118
4564
  // src/docs/generator.ts
4119
4565
  import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync12 } from "fs";
4120
- import { join as join12 } from "path";
4566
+ import { join as join14 } from "path";
4121
4567
 
4122
4568
  // src/docs/architecture.ts
4123
4569
  import { dirname as dirname9 } from "path";
@@ -4519,7 +4965,7 @@ function detectCycles(graph) {
4519
4965
  }
4520
4966
 
4521
4967
  // src/docs/conventions.ts
4522
- import { basename, extname as extname4 } from "path";
4968
+ import { basename as basename2, extname as extname4 } from "path";
4523
4969
  function generateConventions(graph, projectRoot, version) {
4524
4970
  let output = "";
4525
4971
  const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -4557,7 +5003,7 @@ function generateFileOrganization(graph) {
4557
5003
  graph.forEachNode((node, attrs) => {
4558
5004
  if (!files.has(attrs.filePath)) {
4559
5005
  files.add(attrs.filePath);
4560
- const fileName = basename(attrs.filePath);
5006
+ const fileName = basename2(attrs.filePath);
4561
5007
  if (fileName === "index.ts" || fileName === "index.js" || fileName === "index.tsx" || fileName === "index.jsx") {
4562
5008
  barrelFileCount++;
4563
5009
  }
@@ -4616,7 +5062,7 @@ function generateNamingPatterns(graph) {
4616
5062
  graph.forEachNode((node, attrs) => {
4617
5063
  if (!files.has(attrs.filePath)) {
4618
5064
  files.add(attrs.filePath);
4619
- const fileName = basename(attrs.filePath, extname4(attrs.filePath));
5065
+ const fileName = basename2(attrs.filePath, extname4(attrs.filePath));
4620
5066
  if (isCamelCase(fileName)) patterns.files.camelCase++;
4621
5067
  else if (isPascalCase(fileName)) patterns.files.PascalCase++;
4622
5068
  else if (isKebabCase(fileName)) patterns.files.kebabCase++;
@@ -5681,7 +6127,7 @@ function generateDepwireUsage(projectRoot) {
5681
6127
  }
5682
6128
 
5683
6129
  // src/docs/files.ts
5684
- import { dirname as dirname11, basename as basename2 } from "path";
6130
+ import { dirname as dirname11, basename as basename3 } from "path";
5685
6131
  function generateFiles(graph, projectRoot, version) {
5686
6132
  let output = "";
5687
6133
  const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -5800,7 +6246,7 @@ function generateDirectoryBreakdown(graph) {
5800
6246
  dirStats.symbolCount += file.symbolCount;
5801
6247
  if (file.totalConnections > dirStats.maxConnections) {
5802
6248
  dirStats.maxConnections = file.totalConnections;
5803
- dirStats.mostConnectedFile = basename2(file.filePath);
6249
+ dirStats.mostConnectedFile = basename3(file.filePath);
5804
6250
  }
5805
6251
  }
5806
6252
  if (dirMap.size === 0) {
@@ -6428,7 +6874,7 @@ function generateRecommendations(graph) {
6428
6874
  }
6429
6875
 
6430
6876
  // src/docs/tests.ts
6431
- import { basename as basename3, dirname as dirname12 } from "path";
6877
+ import { basename as basename4, dirname as dirname12 } from "path";
6432
6878
  function generateTests(graph, projectRoot, version) {
6433
6879
  let output = "";
6434
6880
  const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -6456,7 +6902,7 @@ function getFileCount8(graph) {
6456
6902
  return files.size;
6457
6903
  }
6458
6904
  function isTestFile3(filePath) {
6459
- const fileName = basename3(filePath).toLowerCase();
6905
+ const fileName = basename4(filePath).toLowerCase();
6460
6906
  const dirPath = dirname12(filePath).toLowerCase();
6461
6907
  if (dirPath.includes("test") || dirPath.includes("spec") || dirPath.includes("__tests__")) {
6462
6908
  return true;
@@ -6515,7 +6961,7 @@ function generateTestFileInventory(graph) {
6515
6961
  return output;
6516
6962
  }
6517
6963
  function matchTestToSource(testFile) {
6518
- const testFileName = basename3(testFile);
6964
+ const testFileName = basename4(testFile);
6519
6965
  const testDir = dirname12(testFile);
6520
6966
  let sourceFileName = testFileName.replace(/\.test\./g, ".").replace(/\.spec\./g, ".").replace(/_test\./g, ".").replace(/_spec\./g, ".");
6521
6967
  const possiblePaths = [];
@@ -6677,7 +7123,7 @@ function generateTestCoverageMap(graph) {
6677
7123
  const rows = mappings.slice(0, 30).map((m) => [
6678
7124
  `\`${m.sourceFile}\``,
6679
7125
  m.hasTest ? "\u2705" : "\u274C",
6680
- m.testFile ? `\`${basename3(m.testFile)}\`` : "-",
7126
+ m.testFile ? `\`${basename4(m.testFile)}\`` : "-",
6681
7127
  formatNumber(m.symbolCount)
6682
7128
  ]);
6683
7129
  let output = table(headers, rows);
@@ -7453,8 +7899,8 @@ function getTopLevelDir2(filePath) {
7453
7899
  }
7454
7900
 
7455
7901
  // src/docs/status.ts
7456
- import { readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
7457
- import { resolve as resolve5 } from "path";
7902
+ import { readFileSync as readFileSync10, existsSync as existsSync10 } from "fs";
7903
+ import { resolve as resolve7 } from "path";
7458
7904
  function generateStatus(graph, projectRoot, version) {
7459
7905
  let output = "";
7460
7906
  const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -7487,8 +7933,8 @@ function getFileCount11(graph) {
7487
7933
  }
7488
7934
  function extractComments(projectRoot, filePath) {
7489
7935
  const comments = [];
7490
- const resolvedRoot = resolve5(projectRoot);
7491
- const fullPath = resolve5(resolvedRoot, filePath);
7936
+ const resolvedRoot = resolve7(projectRoot);
7937
+ const fullPath = resolve7(resolvedRoot, filePath);
7492
7938
  if (!fullPath.startsWith(resolvedRoot)) {
7493
7939
  return comments;
7494
7940
  }
@@ -7496,7 +7942,7 @@ function extractComments(projectRoot, filePath) {
7496
7942
  return comments;
7497
7943
  }
7498
7944
  try {
7499
- const content = readFileSync8(fullPath, "utf-8");
7945
+ const content = readFileSync10(fullPath, "utf-8");
7500
7946
  const lines = content.split("\n");
7501
7947
  const patterns = [
7502
7948
  { type: "TODO", regex: /(?:\/\/|#|\/\*)\s*TODO:?\s*(.+)/i },
@@ -8075,16 +8521,16 @@ function generateConfidenceSection(title, description, symbols, projectRoot) {
8075
8521
  }
8076
8522
 
8077
8523
  // src/docs/metadata.ts
8078
- import { existsSync as existsSync11, readFileSync as readFileSync9, writeFileSync as writeFileSync2 } from "fs";
8079
- import { resolve as resolve6 } from "path";
8524
+ import { existsSync as existsSync11, readFileSync as readFileSync11, writeFileSync as writeFileSync2 } from "fs";
8525
+ import { resolve as resolve8 } from "path";
8080
8526
  function loadMetadata(outputDir) {
8081
- const resolvedDir = resolve6(outputDir);
8082
- const metadataPath = resolve6(resolvedDir, "metadata.json");
8527
+ const resolvedDir = resolve8(outputDir);
8528
+ const metadataPath = resolve8(resolvedDir, "metadata.json");
8083
8529
  if (!metadataPath.startsWith(resolvedDir) || !existsSync11(metadataPath)) {
8084
8530
  return null;
8085
8531
  }
8086
8532
  try {
8087
- const content = readFileSync9(metadataPath, "utf-8");
8533
+ const content = readFileSync11(metadataPath, "utf-8");
8088
8534
  return JSON.parse(content);
8089
8535
  } catch (err) {
8090
8536
  console.error("Failed to load metadata:", err);
@@ -8092,8 +8538,8 @@ function loadMetadata(outputDir) {
8092
8538
  }
8093
8539
  }
8094
8540
  function saveMetadata(outputDir, metadata) {
8095
- const resolvedDir = resolve6(outputDir);
8096
- const metadataPath = resolve6(resolvedDir, "metadata.json");
8541
+ const resolvedDir = resolve8(outputDir);
8542
+ const metadataPath = resolve8(resolvedDir, "metadata.json");
8097
8543
  if (!metadataPath.startsWith(resolvedDir)) {
8098
8544
  throw new Error(`Path traversal attempt blocked: ${metadataPath}`);
8099
8545
  }
@@ -8178,7 +8624,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8178
8624
  try {
8179
8625
  if (options.verbose) console.log("Generating ARCHITECTURE.md...");
8180
8626
  const content = generateArchitecture(graph, projectRoot, version, parseTime);
8181
- const filePath = join12(options.outputDir, "ARCHITECTURE.md");
8627
+ const filePath = join14(options.outputDir, "ARCHITECTURE.md");
8182
8628
  writeFileSync3(filePath, content, "utf-8");
8183
8629
  generated.push("ARCHITECTURE.md");
8184
8630
  } catch (err) {
@@ -8189,7 +8635,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8189
8635
  try {
8190
8636
  if (options.verbose) console.log("Generating CONVENTIONS.md...");
8191
8637
  const content = generateConventions(graph, projectRoot, version);
8192
- const filePath = join12(options.outputDir, "CONVENTIONS.md");
8638
+ const filePath = join14(options.outputDir, "CONVENTIONS.md");
8193
8639
  writeFileSync3(filePath, content, "utf-8");
8194
8640
  generated.push("CONVENTIONS.md");
8195
8641
  } catch (err) {
@@ -8200,7 +8646,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8200
8646
  try {
8201
8647
  if (options.verbose) console.log("Generating DEPENDENCIES.md...");
8202
8648
  const content = generateDependencies(graph, projectRoot, version);
8203
- const filePath = join12(options.outputDir, "DEPENDENCIES.md");
8649
+ const filePath = join14(options.outputDir, "DEPENDENCIES.md");
8204
8650
  writeFileSync3(filePath, content, "utf-8");
8205
8651
  generated.push("DEPENDENCIES.md");
8206
8652
  } catch (err) {
@@ -8211,7 +8657,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8211
8657
  try {
8212
8658
  if (options.verbose) console.log("Generating ONBOARDING.md...");
8213
8659
  const content = generateOnboarding(graph, projectRoot, version);
8214
- const filePath = join12(options.outputDir, "ONBOARDING.md");
8660
+ const filePath = join14(options.outputDir, "ONBOARDING.md");
8215
8661
  writeFileSync3(filePath, content, "utf-8");
8216
8662
  generated.push("ONBOARDING.md");
8217
8663
  } catch (err) {
@@ -8222,7 +8668,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8222
8668
  try {
8223
8669
  if (options.verbose) console.log("Generating FILES.md...");
8224
8670
  const content = generateFiles(graph, projectRoot, version);
8225
- const filePath = join12(options.outputDir, "FILES.md");
8671
+ const filePath = join14(options.outputDir, "FILES.md");
8226
8672
  writeFileSync3(filePath, content, "utf-8");
8227
8673
  generated.push("FILES.md");
8228
8674
  } catch (err) {
@@ -8233,7 +8679,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8233
8679
  try {
8234
8680
  if (options.verbose) console.log("Generating API_SURFACE.md...");
8235
8681
  const content = generateApiSurface(graph, projectRoot, version);
8236
- const filePath = join12(options.outputDir, "API_SURFACE.md");
8682
+ const filePath = join14(options.outputDir, "API_SURFACE.md");
8237
8683
  writeFileSync3(filePath, content, "utf-8");
8238
8684
  generated.push("API_SURFACE.md");
8239
8685
  } catch (err) {
@@ -8244,7 +8690,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8244
8690
  try {
8245
8691
  if (options.verbose) console.log("Generating ERRORS.md...");
8246
8692
  const content = generateErrors(graph, projectRoot, version);
8247
- const filePath = join12(options.outputDir, "ERRORS.md");
8693
+ const filePath = join14(options.outputDir, "ERRORS.md");
8248
8694
  writeFileSync3(filePath, content, "utf-8");
8249
8695
  generated.push("ERRORS.md");
8250
8696
  } catch (err) {
@@ -8255,7 +8701,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8255
8701
  try {
8256
8702
  if (options.verbose) console.log("Generating TESTS.md...");
8257
8703
  const content = generateTests(graph, projectRoot, version);
8258
- const filePath = join12(options.outputDir, "TESTS.md");
8704
+ const filePath = join14(options.outputDir, "TESTS.md");
8259
8705
  writeFileSync3(filePath, content, "utf-8");
8260
8706
  generated.push("TESTS.md");
8261
8707
  } catch (err) {
@@ -8266,7 +8712,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8266
8712
  try {
8267
8713
  if (options.verbose) console.log("Generating HISTORY.md...");
8268
8714
  const content = generateHistory(graph, projectRoot, version);
8269
- const filePath = join12(options.outputDir, "HISTORY.md");
8715
+ const filePath = join14(options.outputDir, "HISTORY.md");
8270
8716
  writeFileSync3(filePath, content, "utf-8");
8271
8717
  generated.push("HISTORY.md");
8272
8718
  } catch (err) {
@@ -8277,7 +8723,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8277
8723
  try {
8278
8724
  if (options.verbose) console.log("Generating CURRENT.md...");
8279
8725
  const content = generateCurrent(graph, projectRoot, version);
8280
- const filePath = join12(options.outputDir, "CURRENT.md");
8726
+ const filePath = join14(options.outputDir, "CURRENT.md");
8281
8727
  writeFileSync3(filePath, content, "utf-8");
8282
8728
  generated.push("CURRENT.md");
8283
8729
  } catch (err) {
@@ -8288,7 +8734,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8288
8734
  try {
8289
8735
  if (options.verbose) console.log("Generating STATUS.md...");
8290
8736
  const content = generateStatus(graph, projectRoot, version);
8291
- const filePath = join12(options.outputDir, "STATUS.md");
8737
+ const filePath = join14(options.outputDir, "STATUS.md");
8292
8738
  writeFileSync3(filePath, content, "utf-8");
8293
8739
  generated.push("STATUS.md");
8294
8740
  } catch (err) {
@@ -8299,7 +8745,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8299
8745
  try {
8300
8746
  if (options.verbose) console.log("Generating HEALTH.md...");
8301
8747
  const content = generateHealth(graph, projectRoot, version);
8302
- const filePath = join12(options.outputDir, "HEALTH.md");
8748
+ const filePath = join14(options.outputDir, "HEALTH.md");
8303
8749
  writeFileSync3(filePath, content, "utf-8");
8304
8750
  generated.push("HEALTH.md");
8305
8751
  } catch (err) {
@@ -8310,7 +8756,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
8310
8756
  try {
8311
8757
  if (options.verbose) console.log("Generating DEAD_CODE.md...");
8312
8758
  const content = generateDeadCode(graph, projectRoot, version);
8313
- const filePath = join12(options.outputDir, "DEAD_CODE.md");
8759
+ const filePath = join14(options.outputDir, "DEAD_CODE.md");
8314
8760
  writeFileSync3(filePath, content, "utf-8");
8315
8761
  generated.push("DEAD_CODE.md");
8316
8762
  } catch (err) {
@@ -8354,13 +8800,13 @@ function getFileCount13(graph) {
8354
8800
  }
8355
8801
 
8356
8802
  // src/simulation/engine.ts
8357
- import { dirname as dirname15, join as join13 } from "path";
8358
- function normalizePath(p) {
8803
+ import { dirname as dirname15, join as join15 } from "path";
8804
+ function normalizePath2(p) {
8359
8805
  return p.replace(/^\.\//, "").replace(/\/+$/, "");
8360
8806
  }
8361
8807
  function fileMatch(nodeFilePath, target) {
8362
- const a = normalizePath(nodeFilePath);
8363
- const b = normalizePath(target);
8808
+ const a = normalizePath2(nodeFilePath);
8809
+ const b = normalizePath2(target);
8364
8810
  return a === b || a.endsWith("/" + b) || b.endsWith("/" + a);
8365
8811
  }
8366
8812
  var SimulationEngine = class {
@@ -8425,8 +8871,8 @@ var SimulationEngine = class {
8425
8871
  }
8426
8872
  // ── Action implementations ─────────────────────────────────────
8427
8873
  applyMove(clone, target, destination, brokenImports) {
8428
- const normalizedTarget = normalizePath(target);
8429
- const normalizedDest = normalizePath(destination);
8874
+ const normalizedTarget = normalizePath2(target);
8875
+ const normalizedDest = normalizePath2(destination);
8430
8876
  const nodesToMove = clone.filterNodes(
8431
8877
  (_node, attrs) => fileMatch(attrs.filePath, target)
8432
8878
  );
@@ -8485,11 +8931,11 @@ var SimulationEngine = class {
8485
8931
  }
8486
8932
  }
8487
8933
  applyRename(clone, target, newName, brokenImports) {
8488
- const destination = join13(dirname15(target), newName);
8934
+ const destination = join15(dirname15(target), newName);
8489
8935
  this.applyMove(clone, target, destination, brokenImports);
8490
8936
  }
8491
8937
  applySplit(clone, target, newFile, symbols, brokenImports) {
8492
- const normalizedNewFile = normalizePath(newFile);
8938
+ const normalizedNewFile = normalizePath2(newFile);
8493
8939
  const nodesToSplit = clone.filterNodes((_node, attrs) => {
8494
8940
  return fileMatch(attrs.filePath, target) && symbols.includes(attrs.name);
8495
8941
  });
@@ -8525,7 +8971,7 @@ var SimulationEngine = class {
8525
8971
  }
8526
8972
  }
8527
8973
  applyMerge(clone, target, source, brokenImports) {
8528
- const normalizedTarget = normalizePath(target);
8974
+ const normalizedTarget = normalizePath2(target);
8529
8975
  const sourceNodes = clone.filterNodes(
8530
8976
  (_node, attrs) => fileMatch(attrs.filePath, source)
8531
8977
  );
@@ -8686,12 +9132,12 @@ var SimulationEngine = class {
8686
9132
 
8687
9133
  // src/security/scanner.ts
8688
9134
  import { existsSync as existsSync14 } from "fs";
8689
- import { join as join23 } from "path";
9135
+ import { join as join25 } from "path";
8690
9136
 
8691
9137
  // src/security/checks/dependencies.ts
8692
9138
  import { execSync as execSync2 } from "child_process";
8693
- import { existsSync as existsSync13, readFileSync as readFileSync10, readdirSync as readdirSync5 } from "fs";
8694
- import { join as join14 } from "path";
9139
+ import { existsSync as existsSync13, readFileSync as readFileSync12, readdirSync as readdirSync5 } from "fs";
9140
+ import { join as join16 } from "path";
8695
9141
  function cvssToSeverity(score) {
8696
9142
  if (score >= 9) return "critical";
8697
9143
  if (score >= 7) return "high";
@@ -8701,18 +9147,18 @@ function cvssToSeverity(score) {
8701
9147
  async function checkDependencies(_files, projectRoot) {
8702
9148
  const findings = [];
8703
9149
  try {
8704
- if (existsSync13(join14(projectRoot, "package.json"))) {
9150
+ if (existsSync13(join16(projectRoot, "package.json"))) {
8705
9151
  findings.push(...checkNpmAudit(projectRoot));
8706
9152
  findings.push(...checkPackageJsonPatterns(projectRoot));
8707
9153
  findings.push(...checkPostinstallScripts(projectRoot));
8708
9154
  }
8709
- if (existsSync13(join14(projectRoot, "requirements.txt")) || existsSync13(join14(projectRoot, "pyproject.toml"))) {
9155
+ if (existsSync13(join16(projectRoot, "requirements.txt")) || existsSync13(join16(projectRoot, "pyproject.toml"))) {
8710
9156
  findings.push(...checkPipAudit(projectRoot));
8711
9157
  }
8712
- if (existsSync13(join14(projectRoot, "Cargo.toml"))) {
9158
+ if (existsSync13(join16(projectRoot, "Cargo.toml"))) {
8713
9159
  findings.push(...checkCargoAudit(projectRoot));
8714
9160
  }
8715
- if (existsSync13(join14(projectRoot, "go.mod"))) {
9161
+ if (existsSync13(join16(projectRoot, "go.mod"))) {
8716
9162
  findings.push(...checkGoVerify(projectRoot));
8717
9163
  }
8718
9164
  } catch (err) {
@@ -8801,8 +9247,8 @@ function checkNpmAudit(projectRoot) {
8801
9247
  function checkPackageJsonPatterns(projectRoot) {
8802
9248
  const findings = [];
8803
9249
  try {
8804
- const pkgPath = join14(projectRoot, "package.json");
8805
- const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
9250
+ const pkgPath = join16(projectRoot, "package.json");
9251
+ const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
8806
9252
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
8807
9253
  for (const [name, version] of Object.entries(allDeps)) {
8808
9254
  if (version.startsWith("^") || version.startsWith("~")) {
@@ -8824,15 +9270,15 @@ function checkPackageJsonPatterns(projectRoot) {
8824
9270
  }
8825
9271
  function checkPostinstallScripts(projectRoot) {
8826
9272
  const findings = [];
8827
- const nodeModules = join14(projectRoot, "node_modules");
9273
+ const nodeModules = join16(projectRoot, "node_modules");
8828
9274
  if (!existsSync13(nodeModules)) return findings;
8829
9275
  try {
8830
9276
  const topLevelDeps = readdirSync5(nodeModules).filter((d) => !d.startsWith("."));
8831
9277
  for (const dep of topLevelDeps) {
8832
- const depPkgPath = join14(nodeModules, dep, "package.json");
9278
+ const depPkgPath = join16(nodeModules, dep, "package.json");
8833
9279
  if (!existsSync13(depPkgPath)) continue;
8834
9280
  try {
8835
- const depPkg = JSON.parse(readFileSync10(depPkgPath, "utf-8"));
9281
+ const depPkg = JSON.parse(readFileSync12(depPkgPath, "utf-8"));
8836
9282
  const scripts = depPkg.scripts || {};
8837
9283
  if (scripts.postinstall || scripts.preinstall || scripts.install) {
8838
9284
  const scriptName = scripts.postinstall ? "postinstall" : scripts.preinstall ? "preinstall" : "install";
@@ -8870,7 +9316,7 @@ function checkPipAudit(projectRoot) {
8870
9316
  id: "",
8871
9317
  severity: cvssToSeverity(vuln.cvss?.score || 5),
8872
9318
  vulnerabilityClass: "dependency-cve",
8873
- file: existsSync13(join14(projectRoot, "requirements.txt")) ? "requirements.txt" : "pyproject.toml",
9319
+ file: existsSync13(join16(projectRoot, "requirements.txt")) ? "requirements.txt" : "pyproject.toml",
8874
9320
  title: `Vulnerable Python dependency: ${vuln.name}`,
8875
9321
  description: `${vuln.name}@${vuln.version} \u2014 ${vuln.id}: ${vuln.description || "Known vulnerability"}`,
8876
9322
  attackScenario: `An attacker could exploit the vulnerability in ${vuln.name}.`,
@@ -8967,8 +9413,8 @@ function checkGoVerify(projectRoot) {
8967
9413
  }
8968
9414
 
8969
9415
  // src/security/checks/injection.ts
8970
- import { readFileSync as readFileSync11 } from "fs";
8971
- import { join as join15 } from "path";
9416
+ import { readFileSync as readFileSync13 } from "fs";
9417
+ import { join as join17 } from "path";
8972
9418
  var SKIP_DIRS = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
8973
9419
  var TEST_PATTERNS = ["test", "spec", "fixture", "mock", "__tests__", "__mocks__"];
8974
9420
  var USER_INPUT_NAMES = /(?:input|user|name|path|query|branch|hash|cmd|command|req\.|params|body|args|url|dir|file|subdirectory)/i;
@@ -9069,7 +9515,7 @@ async function checkInjection(files, projectRoot) {
9069
9515
  if (shouldSkip(file.filePath) || isTestFile4(file.filePath)) continue;
9070
9516
  let content;
9071
9517
  try {
9072
- content = readFileSync11(join15(projectRoot, file.filePath), "utf-8");
9518
+ content = readFileSync13(join17(projectRoot, file.filePath), "utf-8");
9073
9519
  } catch {
9074
9520
  continue;
9075
9521
  }
@@ -9107,8 +9553,8 @@ async function checkInjection(files, projectRoot) {
9107
9553
  }
9108
9554
 
9109
9555
  // src/security/checks/secrets.ts
9110
- import { readFileSync as readFileSync12 } from "fs";
9111
- import { join as join16 } from "path";
9556
+ import { readFileSync as readFileSync14 } from "fs";
9557
+ import { join as join18 } from "path";
9112
9558
  var SKIP_DIRS2 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
9113
9559
  var TEST_PATTERNS2 = ["test", "spec", "fixture", "mock", "__tests__", "__mocks__", ".example", ".sample"];
9114
9560
  var SECRET_PATTERNS = [
@@ -9141,7 +9587,7 @@ async function checkSecrets(files, projectRoot) {
9141
9587
  if (shouldSkip2(file.filePath) || isTestFile5(file.filePath)) continue;
9142
9588
  let content;
9143
9589
  try {
9144
- content = readFileSync12(join16(projectRoot, file.filePath), "utf-8");
9590
+ content = readFileSync14(join18(projectRoot, file.filePath), "utf-8");
9145
9591
  } catch {
9146
9592
  continue;
9147
9593
  }
@@ -9174,8 +9620,8 @@ async function checkSecrets(files, projectRoot) {
9174
9620
  }
9175
9621
 
9176
9622
  // src/security/checks/path-traversal.ts
9177
- import { readFileSync as readFileSync13 } from "fs";
9178
- import { join as join17 } from "path";
9623
+ import { readFileSync as readFileSync15 } from "fs";
9624
+ import { join as join19 } from "path";
9179
9625
  var SKIP_DIRS3 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
9180
9626
  var USER_INPUT_VARS = /(?:req\.|params|query|body|input|path|dir|subdirectory|file|userInput|fileName|filePath)/i;
9181
9627
  var PATTERNS2 = [
@@ -9222,7 +9668,7 @@ async function checkPathTraversal(files, projectRoot) {
9222
9668
  if (shouldSkip3(file.filePath)) continue;
9223
9669
  let content;
9224
9670
  try {
9225
- content = readFileSync13(join17(projectRoot, file.filePath), "utf-8");
9671
+ content = readFileSync15(join19(projectRoot, file.filePath), "utf-8");
9226
9672
  } catch {
9227
9673
  continue;
9228
9674
  }
@@ -9264,8 +9710,8 @@ async function checkPathTraversal(files, projectRoot) {
9264
9710
  }
9265
9711
 
9266
9712
  // src/security/checks/auth.ts
9267
- import { readFileSync as readFileSync14 } from "fs";
9268
- import { join as join18 } from "path";
9713
+ import { readFileSync as readFileSync16 } from "fs";
9714
+ import { join as join20 } from "path";
9269
9715
  var SKIP_DIRS4 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
9270
9716
  function shouldSkip4(filePath) {
9271
9717
  return SKIP_DIRS4.some((d) => filePath.includes(d));
@@ -9281,7 +9727,7 @@ async function checkAuth(files, projectRoot) {
9281
9727
  if (shouldSkip4(file.filePath)) continue;
9282
9728
  let content;
9283
9729
  try {
9284
- content = readFileSync14(join18(projectRoot, file.filePath), "utf-8");
9730
+ content = readFileSync16(join20(projectRoot, file.filePath), "utf-8");
9285
9731
  } catch {
9286
9732
  continue;
9287
9733
  }
@@ -9372,8 +9818,8 @@ async function checkAuth(files, projectRoot) {
9372
9818
  }
9373
9819
 
9374
9820
  // src/security/checks/input-validation.ts
9375
- import { readFileSync as readFileSync15 } from "fs";
9376
- import { join as join19 } from "path";
9821
+ import { readFileSync as readFileSync17 } from "fs";
9822
+ import { join as join21 } from "path";
9377
9823
  var SKIP_DIRS5 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
9378
9824
  function shouldSkip5(filePath) {
9379
9825
  return SKIP_DIRS5.some((d) => filePath.includes(d));
@@ -9385,7 +9831,7 @@ async function checkInputValidation(files, projectRoot) {
9385
9831
  if (shouldSkip5(file.filePath)) continue;
9386
9832
  let content;
9387
9833
  try {
9388
- content = readFileSync15(join19(projectRoot, file.filePath), "utf-8");
9834
+ content = readFileSync17(join21(projectRoot, file.filePath), "utf-8");
9389
9835
  } catch {
9390
9836
  continue;
9391
9837
  }
@@ -9462,8 +9908,8 @@ async function checkInputValidation(files, projectRoot) {
9462
9908
  }
9463
9909
 
9464
9910
  // src/security/checks/information-disclosure.ts
9465
- import { readFileSync as readFileSync16 } from "fs";
9466
- import { join as join20 } from "path";
9911
+ import { readFileSync as readFileSync18 } from "fs";
9912
+ import { join as join22 } from "path";
9467
9913
  var SKIP_DIRS6 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
9468
9914
  function shouldSkip6(filePath) {
9469
9915
  return SKIP_DIRS6.some((d) => filePath.includes(d));
@@ -9475,7 +9921,7 @@ async function checkInformationDisclosure(files, projectRoot) {
9475
9921
  if (shouldSkip6(file.filePath)) continue;
9476
9922
  let content;
9477
9923
  try {
9478
- content = readFileSync16(join20(projectRoot, file.filePath), "utf-8");
9924
+ content = readFileSync18(join22(projectRoot, file.filePath), "utf-8");
9479
9925
  } catch {
9480
9926
  continue;
9481
9927
  }
@@ -9545,8 +9991,8 @@ async function checkInformationDisclosure(files, projectRoot) {
9545
9991
  }
9546
9992
 
9547
9993
  // src/security/checks/cryptography.ts
9548
- import { readFileSync as readFileSync17 } from "fs";
9549
- import { join as join21 } from "path";
9994
+ import { readFileSync as readFileSync19 } from "fs";
9995
+ import { join as join23 } from "path";
9550
9996
  var SKIP_DIRS7 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
9551
9997
  function shouldSkip7(filePath) {
9552
9998
  return SKIP_DIRS7.some((d) => filePath.includes(d));
@@ -9562,7 +10008,7 @@ async function checkCryptography(files, projectRoot) {
9562
10008
  if (shouldSkip7(file.filePath)) continue;
9563
10009
  let content;
9564
10010
  try {
9565
- content = readFileSync17(join21(projectRoot, file.filePath), "utf-8");
10011
+ content = readFileSync19(join23(projectRoot, file.filePath), "utf-8");
9566
10012
  } catch {
9567
10013
  continue;
9568
10014
  }
@@ -9644,8 +10090,8 @@ async function checkCryptography(files, projectRoot) {
9644
10090
  }
9645
10091
 
9646
10092
  // src/security/checks/frontend.ts
9647
- import { readFileSync as readFileSync18 } from "fs";
9648
- import { join as join22 } from "path";
10093
+ import { readFileSync as readFileSync20 } from "fs";
10094
+ import { join as join24 } from "path";
9649
10095
  var SKIP_DIRS8 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
9650
10096
  function shouldSkip8(filePath) {
9651
10097
  return SKIP_DIRS8.some((d) => filePath.includes(d));
@@ -9661,7 +10107,7 @@ async function checkFrontend(files, projectRoot) {
9661
10107
  if (!isFrontendFile(file.filePath)) continue;
9662
10108
  let content;
9663
10109
  try {
9664
- content = readFileSync18(join22(projectRoot, file.filePath), "utf-8");
10110
+ content = readFileSync20(join24(projectRoot, file.filePath), "utf-8");
9665
10111
  } catch {
9666
10112
  continue;
9667
10113
  }
@@ -10121,11 +10567,11 @@ async function scanSecurity(projectRoot, graph, options = {}) {
10121
10567
  };
10122
10568
  }
10123
10569
  function detectPackageManager(projectRoot) {
10124
- if (existsSync14(join23(projectRoot, "package.json"))) return "npm";
10125
- if (existsSync14(join23(projectRoot, "requirements.txt"))) return "pip";
10126
- if (existsSync14(join23(projectRoot, "pyproject.toml"))) return "pip";
10127
- if (existsSync14(join23(projectRoot, "Cargo.toml"))) return "cargo";
10128
- if (existsSync14(join23(projectRoot, "go.mod"))) return "go";
10570
+ if (existsSync14(join25(projectRoot, "package.json"))) return "npm";
10571
+ if (existsSync14(join25(projectRoot, "requirements.txt"))) return "pip";
10572
+ if (existsSync14(join25(projectRoot, "pyproject.toml"))) return "pip";
10573
+ if (existsSync14(join25(projectRoot, "Cargo.toml"))) return "cargo";
10574
+ if (existsSync14(join25(projectRoot, "go.mod"))) return "go";
10129
10575
  return "unknown";
10130
10576
  }
10131
10577
 
@@ -10133,6 +10579,7 @@ export {
10133
10579
  findProjectRoot,
10134
10580
  parseTypeScriptFile,
10135
10581
  parseProject,
10582
+ detectCrossLanguageEdges,
10136
10583
  buildGraph,
10137
10584
  findSymbols,
10138
10585
  getDependencies,