@vohongtho.infotech/code-intel 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createRequire } from 'module';
2
2
  import { fileURLToPath } from 'url';
3
- import path29 from 'path';
4
- import fs23, { existsSync } from 'fs';
3
+ import path31 from 'path';
4
+ import fs24, { existsSync } from 'fs';
5
5
  import { Parser, Language, Query } from 'web-tree-sitter';
6
6
  import { NodeSDK } from '@opentelemetry/sdk-node';
7
7
  import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
@@ -126,11 +126,11 @@ var init_shared = __esm({
126
126
  }
127
127
  });
128
128
  function findBundledWasmDir() {
129
- const fileDir = path29.dirname(fileURLToPath(import.meta.url));
129
+ const fileDir = path31.dirname(fileURLToPath(import.meta.url));
130
130
  const candidates = [
131
- path29.join(fileDir, "wasm"),
131
+ path31.join(fileDir, "wasm"),
132
132
  // dist/index.js → dist/wasm/
133
- path29.join(fileDir, "../wasm")
133
+ path31.join(fileDir, "../wasm")
134
134
  // dist/cli/main.js → dist/wasm/
135
135
  ];
136
136
  for (const candidate of candidates) {
@@ -171,7 +171,7 @@ function wasmPath(lang) {
171
171
  }
172
172
  const bundled = BUNDLED_WASM_MAP[lang];
173
173
  if (bundled) {
174
- const bundledPath = path29.join(_bundledWasmDir, bundled);
174
+ const bundledPath = path31.join(_bundledWasmDir, bundled);
175
175
  if (existsSync(bundledPath)) return bundledPath;
176
176
  }
177
177
  return null;
@@ -184,14 +184,14 @@ async function initParser() {
184
184
  }
185
185
  async function getLanguage(lang) {
186
186
  if (languageCache.has(lang)) return languageCache.get(lang);
187
- const path30 = wasmPath(lang);
188
- if (!path30) {
187
+ const path32 = wasmPath(lang);
188
+ if (!path32) {
189
189
  languageCache.set(lang, null);
190
190
  return null;
191
191
  }
192
192
  try {
193
193
  await initParser();
194
- const language = await Language.load(path30);
194
+ const language = await Language.load(path32);
195
195
  languageCache.set(lang, language);
196
196
  return language;
197
197
  } catch {
@@ -1162,7 +1162,7 @@ var init_logger = __esm({
1162
1162
  };
1163
1163
  }
1164
1164
  /** Global log directory: ~/.code-intel/logs */
1165
- static LOG_DIR = path29.join(os12.homedir(), ".code-intel", "logs");
1165
+ static LOG_DIR = path31.join(os12.homedir(), ".code-intel", "logs");
1166
1166
  static getLogger() {
1167
1167
  if (!_Logger.instance) {
1168
1168
  const isProduction = process.env.NODE_ENV === "production";
@@ -1171,12 +1171,12 @@ var init_logger = __esm({
1171
1171
  transports.push(new winston.transports.Console());
1172
1172
  if (!isProduction) {
1173
1173
  try {
1174
- if (!fs23.existsSync(_Logger.LOG_DIR)) {
1175
- fs23.mkdirSync(_Logger.LOG_DIR, { recursive: true });
1174
+ if (!fs24.existsSync(_Logger.LOG_DIR)) {
1175
+ fs24.mkdirSync(_Logger.LOG_DIR, { recursive: true });
1176
1176
  }
1177
1177
  transports.push(
1178
1178
  new DailyRotateFile({
1179
- filename: path29.join(_Logger.LOG_DIR, "%DATE%-code-intel.log"),
1179
+ filename: path31.join(_Logger.LOG_DIR, "%DATE%-code-intel.log"),
1180
1180
  datePattern: "YYYY-MM-DD",
1181
1181
  maxSize: "20m",
1182
1182
  maxFiles: "14d"
@@ -1961,7 +1961,7 @@ var init_parse_phase = __esm({
1961
1961
  const batch = filePaths.slice(i, i + CONCURRENCY);
1962
1962
  await Promise.all(batch.map(async (filePath) => {
1963
1963
  try {
1964
- const source = await fs23.promises.readFile(filePath, "utf-8");
1964
+ const source = await fs24.promises.readFile(filePath, "utf-8");
1965
1965
  context2.fileCache.set(filePath, source);
1966
1966
  } catch {
1967
1967
  }
@@ -1974,14 +1974,14 @@ var init_parse_phase = __esm({
1974
1974
  const lang = detectLanguage(filePath);
1975
1975
  if (!lang) {
1976
1976
  if (context2.verbose) {
1977
- const relativePath2 = path29.relative(context2.workspaceRoot, filePath);
1977
+ const relativePath2 = path31.relative(context2.workspaceRoot, filePath);
1978
1978
  logger_default.info(` [parse] skipped (no parser): ${relativePath2}`);
1979
1979
  }
1980
1980
  continue;
1981
1981
  }
1982
1982
  const source = context2.fileCache.get(filePath);
1983
1983
  if (!source) continue;
1984
- const relativePath = path29.relative(context2.workspaceRoot, filePath);
1984
+ const relativePath = path31.relative(context2.workspaceRoot, filePath);
1985
1985
  const fileNodeId = generateNodeId("file", relativePath, relativePath);
1986
1986
  const fileNode = context2.graph.getNode(fileNodeId);
1987
1987
  if (fileNode) {
@@ -2221,11 +2221,11 @@ var init_resolve_phase = __esm({
2221
2221
  let heritageEdges = 0;
2222
2222
  const fileIndex = /* @__PURE__ */ new Map();
2223
2223
  for (const fp of filePaths) {
2224
- const rel = path29.relative(workspaceRoot, fp);
2224
+ const rel = path31.relative(workspaceRoot, fp);
2225
2225
  fileIndex.set(rel, fp);
2226
2226
  const noExt = rel.replace(/\.\w+$/, "");
2227
2227
  if (!fileIndex.has(noExt)) fileIndex.set(noExt, fp);
2228
- const base = path29.basename(rel, path29.extname(rel));
2228
+ const base = path31.basename(rel, path31.extname(rel));
2229
2229
  if (!fileIndex.has(base)) fileIndex.set(base, fp);
2230
2230
  }
2231
2231
  const symbolIndex = /* @__PURE__ */ new Map();
@@ -2256,7 +2256,7 @@ var init_resolve_phase = __esm({
2256
2256
  for (const filePath of filePaths) {
2257
2257
  const lang = detectLanguage(filePath);
2258
2258
  if (!lang) continue;
2259
- const relativePath = path29.relative(workspaceRoot, filePath);
2259
+ const relativePath = path31.relative(workspaceRoot, filePath);
2260
2260
  const fileNodeId = generateNodeId("file", relativePath, relativePath);
2261
2261
  const source = fileCache.get(filePath);
2262
2262
  if (!source) continue;
@@ -2269,13 +2269,13 @@ var init_resolve_phase = __esm({
2269
2269
  let resolvedRelPath = null;
2270
2270
  if (cleaned.startsWith(".")) {
2271
2271
  const cleanedNoJs = cleaned.replace(/\.(js|jsx)$/, "");
2272
- const fromDir = path29.dirname(relativePath);
2272
+ const fromDir = path31.dirname(relativePath);
2273
2273
  for (const ext of ["", ".ts", ".tsx", ".js", ".jsx", ".py", ".java", ".go", "/index.ts", "/index.js"]) {
2274
- const candidate = path29.join(fromDir, cleanedNoJs + ext);
2275
- const normalized = path29.normalize(candidate);
2274
+ const candidate = path31.join(fromDir, cleanedNoJs + ext);
2275
+ const normalized = path31.normalize(candidate);
2276
2276
  if (fileIndex.has(normalized)) {
2277
2277
  const absPath = fileIndex.get(normalized);
2278
- resolvedRelPath = path29.relative(workspaceRoot, absPath);
2278
+ resolvedRelPath = path31.relative(workspaceRoot, absPath);
2279
2279
  break;
2280
2280
  }
2281
2281
  }
@@ -2589,27 +2589,27 @@ __export(group_registry_exports, {
2589
2589
  saveSyncResult: () => saveSyncResult
2590
2590
  });
2591
2591
  function groupFile(name) {
2592
- return path29.join(GROUPS_DIR, `${name}.json`);
2592
+ return path31.join(GROUPS_DIR, `${name}.json`);
2593
2593
  }
2594
2594
  function loadGroup(name) {
2595
2595
  try {
2596
- return JSON.parse(fs23.readFileSync(groupFile(name), "utf-8"));
2596
+ return JSON.parse(fs24.readFileSync(groupFile(name), "utf-8"));
2597
2597
  } catch {
2598
2598
  return null;
2599
2599
  }
2600
2600
  }
2601
2601
  function saveGroup(group) {
2602
- fs23.mkdirSync(GROUPS_DIR, { recursive: true });
2603
- fs23.writeFileSync(groupFile(group.name), JSON.stringify(group, null, 2) + "\n");
2602
+ fs24.mkdirSync(GROUPS_DIR, { recursive: true });
2603
+ fs24.writeFileSync(groupFile(group.name), JSON.stringify(group, null, 2) + "\n");
2604
2604
  }
2605
2605
  function listGroups() {
2606
2606
  const groups = [];
2607
2607
  try {
2608
- for (const file of fs23.readdirSync(GROUPS_DIR)) {
2608
+ for (const file of fs24.readdirSync(GROUPS_DIR)) {
2609
2609
  if (!file.endsWith(".json") || file.endsWith(".sync.json")) continue;
2610
2610
  try {
2611
2611
  const g = JSON.parse(
2612
- fs23.readFileSync(path29.join(GROUPS_DIR, file), "utf-8")
2612
+ fs24.readFileSync(path31.join(GROUPS_DIR, file), "utf-8")
2613
2613
  );
2614
2614
  groups.push(g);
2615
2615
  } catch {
@@ -2621,16 +2621,16 @@ function listGroups() {
2621
2621
  }
2622
2622
  function deleteGroup(name) {
2623
2623
  try {
2624
- fs23.unlinkSync(groupFile(name));
2624
+ fs24.unlinkSync(groupFile(name));
2625
2625
  } catch {
2626
2626
  }
2627
2627
  try {
2628
- fs23.unlinkSync(path29.join(GROUPS_DIR, `${name}.sync.json`));
2628
+ fs24.unlinkSync(path31.join(GROUPS_DIR, `${name}.sync.json`));
2629
2629
  } catch {
2630
2630
  }
2631
2631
  }
2632
2632
  function groupExists(name) {
2633
- return fs23.existsSync(groupFile(name));
2633
+ return fs24.existsSync(groupFile(name));
2634
2634
  }
2635
2635
  function addMember(groupName, member) {
2636
2636
  const group = loadGroup(groupName);
@@ -2656,16 +2656,16 @@ function removeMember(groupName, groupPath) {
2656
2656
  return group;
2657
2657
  }
2658
2658
  function saveSyncResult(result) {
2659
- fs23.mkdirSync(GROUPS_DIR, { recursive: true });
2660
- fs23.writeFileSync(
2661
- path29.join(GROUPS_DIR, `${result.groupName}.sync.json`),
2659
+ fs24.mkdirSync(GROUPS_DIR, { recursive: true });
2660
+ fs24.writeFileSync(
2661
+ path31.join(GROUPS_DIR, `${result.groupName}.sync.json`),
2662
2662
  JSON.stringify(result, null, 2) + "\n"
2663
2663
  );
2664
2664
  }
2665
2665
  function loadSyncResult(groupName) {
2666
2666
  try {
2667
2667
  return JSON.parse(
2668
- fs23.readFileSync(path29.join(GROUPS_DIR, `${groupName}.sync.json`), "utf-8")
2668
+ fs24.readFileSync(path31.join(GROUPS_DIR, `${groupName}.sync.json`), "utf-8")
2669
2669
  );
2670
2670
  } catch {
2671
2671
  return null;
@@ -2674,7 +2674,7 @@ function loadSyncResult(groupName) {
2674
2674
  var GROUPS_DIR;
2675
2675
  var init_group_registry = __esm({
2676
2676
  "src/multi-repo/group-registry.ts"() {
2677
- GROUPS_DIR = path29.join(os12.homedir(), ".code-intel", "groups");
2677
+ GROUPS_DIR = path31.join(os12.homedir(), ".code-intel", "groups");
2678
2678
  }
2679
2679
  });
2680
2680
 
@@ -2696,14 +2696,14 @@ function detectDeadCode(graph) {
2696
2696
  if (meta?.deprecated === true) continue;
2697
2697
  if (ENTRY_POINT_NAME_RE.test(node.name)) continue;
2698
2698
  if (entryPointIds.has(node.id)) continue;
2699
- let hasCallers = false;
2699
+ let hasCallers2 = false;
2700
2700
  for (const edge of graph.findEdgesTo(node.id)) {
2701
2701
  if (edge.kind === "calls") {
2702
- hasCallers = true;
2702
+ hasCallers2 = true;
2703
2703
  break;
2704
2704
  }
2705
2705
  }
2706
- if (hasCallers) continue;
2706
+ if (hasCallers2) continue;
2707
2707
  let hasImporters = false;
2708
2708
  for (const edge of graph.findEdgesTo(node.id)) {
2709
2709
  if (edge.kind === "imports") {
@@ -3702,6 +3702,550 @@ var init_gql_executor = __esm({
3702
3702
  }
3703
3703
  });
3704
3704
 
3705
+ // src/analysis/deprecated-detector.ts
3706
+ var deprecated_detector_exports = {};
3707
+ __export(deprecated_detector_exports, {
3708
+ DeprecatedDetector: () => DeprecatedDetector
3709
+ });
3710
+ var BUILTIN_DEPRECATED, DeprecatedDetector;
3711
+ var init_deprecated_detector = __esm({
3712
+ "src/analysis/deprecated-detector.ts"() {
3713
+ BUILTIN_DEPRECATED = [
3714
+ { pattern: "url.parse", message: "deprecated in Node.js v11.0.0 \u2014 use the WHATWG URL API instead" },
3715
+ { pattern: "url.resolve", message: "deprecated in Node.js v11.0.0 \u2014 use the WHATWG URL API instead" },
3716
+ { pattern: "url.format", message: "deprecated in Node.js v11.0.0 \u2014 use the WHATWG URL API instead" },
3717
+ { pattern: "fs.exists", message: "deprecated \u2014 use fs.access instead" },
3718
+ { pattern: "crypto.createCipher", message: "deprecated \u2014 use crypto.createCipheriv instead" },
3719
+ { pattern: "crypto.createDecipher", message: "deprecated \u2014 use crypto.createDecipheriv instead" },
3720
+ { pattern: "new Buffer()", message: "deprecated \u2014 use Buffer.from() instead" },
3721
+ { pattern: "domain.create", message: "deprecated \u2014 the domain module is discouraged" },
3722
+ { pattern: "process.binding", message: "deprecated internal API" }
3723
+ ];
3724
+ DeprecatedDetector = class {
3725
+ tagDeprecated(graph) {
3726
+ for (const node of graph.allNodes()) {
3727
+ if (!node.metadata) node.metadata = {};
3728
+ if (node.metadata["deprecated"] === true) continue;
3729
+ let message;
3730
+ const jsdoc = node.metadata["jsdoc"];
3731
+ const comment = node.metadata["comment"];
3732
+ if (jsdoc?.includes("@deprecated") || comment?.includes("@deprecated")) {
3733
+ const src = jsdoc ?? comment ?? "";
3734
+ const match = src.match(/@deprecated\s+(.*)/);
3735
+ message = match?.[1]?.trim() || "deprecated";
3736
+ }
3737
+ if (!message && node.metadata["deprecated"] === true) {
3738
+ message = node.metadata["deprecationMessage"] ?? "deprecated";
3739
+ }
3740
+ if (!message) {
3741
+ const annotations = node.metadata["annotations"];
3742
+ if (Array.isArray(annotations) && annotations.includes("Deprecated")) {
3743
+ message = "marked @Deprecated";
3744
+ }
3745
+ }
3746
+ if (!message) {
3747
+ const attributes = node.metadata["attributes"];
3748
+ if (Array.isArray(attributes) && attributes.includes("deprecated")) {
3749
+ message = "marked #[deprecated]";
3750
+ }
3751
+ }
3752
+ if (!message) {
3753
+ for (const entry of BUILTIN_DEPRECATED) {
3754
+ if (node.name === entry.pattern || node.name.includes(entry.pattern)) {
3755
+ message = entry.message;
3756
+ break;
3757
+ }
3758
+ }
3759
+ }
3760
+ if (message) {
3761
+ node.metadata["deprecated"] = true;
3762
+ node.metadata["deprecationMessage"] = message;
3763
+ }
3764
+ }
3765
+ }
3766
+ detect(graph, scope) {
3767
+ const findings = [];
3768
+ for (const node of graph.allNodes()) {
3769
+ if (!node.metadata?.["deprecated"]) continue;
3770
+ const callers = [];
3771
+ for (const edge of graph.findEdgesTo(node.id)) {
3772
+ if (edge.kind !== "calls" && edge.kind !== "deprecated_use") continue;
3773
+ const caller = graph.getNode(edge.source);
3774
+ if (!caller) continue;
3775
+ if (scope && !caller.filePath.includes(scope)) continue;
3776
+ callers.push({ name: caller.name, filePath: caller.filePath });
3777
+ const edgeId = `dep_use_${edge.source}_${node.id}`;
3778
+ if (!graph.getEdge(edgeId)) {
3779
+ graph.addEdge({ id: edgeId, source: edge.source, target: node.id, kind: "deprecated_use" });
3780
+ }
3781
+ }
3782
+ findings.push({
3783
+ symbol: node.name,
3784
+ filePath: node.filePath,
3785
+ deprecationMessage: node.metadata?.["deprecationMessage"] ?? "deprecated",
3786
+ callers
3787
+ });
3788
+ }
3789
+ return findings;
3790
+ }
3791
+ };
3792
+ }
3793
+ });
3794
+
3795
+ // src/analysis/complexity.ts
3796
+ var complexity_exports = {};
3797
+ __export(complexity_exports, {
3798
+ computeComplexity: () => computeComplexity
3799
+ });
3800
+ function getSeverity(cyclomatic) {
3801
+ if (cyclomatic <= 5) return "LOW";
3802
+ if (cyclomatic <= 10) return "MEDIUM";
3803
+ if (cyclomatic <= 20) return "HIGH";
3804
+ return "CRITICAL";
3805
+ }
3806
+ function computeComplexity(graph, scope) {
3807
+ const results = [];
3808
+ for (const node of graph.allNodes()) {
3809
+ if (node.kind !== "function" && node.kind !== "method") continue;
3810
+ if (scope && !node.filePath.startsWith(scope)) continue;
3811
+ let outgoingCalls = 0;
3812
+ for (const edge of graph.findEdgesFrom(node.id)) {
3813
+ if (edge.kind === "calls") outgoingCalls++;
3814
+ }
3815
+ let cyclomatic;
3816
+ const meta = node.metadata;
3817
+ const metaComplexity = meta?.complexity;
3818
+ if (typeof metaComplexity?.cyclomatic === "number") {
3819
+ cyclomatic = metaComplexity.cyclomatic;
3820
+ } else {
3821
+ cyclomatic = 1 + Math.floor(outgoingCalls / 2);
3822
+ }
3823
+ cyclomatic = Math.min(cyclomatic, 50);
3824
+ let cognitive;
3825
+ if (typeof metaComplexity?.cognitive === "number") {
3826
+ cognitive = metaComplexity.cognitive;
3827
+ } else {
3828
+ cognitive = Math.ceil(cyclomatic * 1.3);
3829
+ }
3830
+ results.push({
3831
+ nodeId: node.id,
3832
+ name: node.name,
3833
+ filePath: node.filePath,
3834
+ cyclomatic,
3835
+ cognitive,
3836
+ severity: getSeverity(cyclomatic)
3837
+ });
3838
+ }
3839
+ return results.sort((a, b) => b.cyclomatic - a.cyclomatic);
3840
+ }
3841
+ var init_complexity = __esm({
3842
+ "src/analysis/complexity.ts"() {
3843
+ }
3844
+ });
3845
+
3846
+ // src/analysis/test-coverage.ts
3847
+ var test_coverage_exports = {};
3848
+ __export(test_coverage_exports, {
3849
+ computeCoverage: () => computeCoverage
3850
+ });
3851
+ function isTestFile(filePath) {
3852
+ if (filePath.includes(".test.") || filePath.includes(".spec.")) return true;
3853
+ if (filePath.includes("_test.") || filePath.endsWith("_test.go")) return true;
3854
+ if (filePath.includes("__tests__")) return true;
3855
+ const base = path31.basename(filePath);
3856
+ if (base.startsWith("Test") && filePath.endsWith(".java")) return true;
3857
+ return false;
3858
+ }
3859
+ function computeBlastRadius(graph, nodeId) {
3860
+ const visited = /* @__PURE__ */ new Set();
3861
+ const queue = [{ id: nodeId, depth: 0 }];
3862
+ while (queue.length > 0) {
3863
+ const { id, depth } = queue.shift();
3864
+ if (visited.has(id)) continue;
3865
+ visited.add(id);
3866
+ if (depth >= 3) continue;
3867
+ for (const edge of graph.findEdgesTo(id)) {
3868
+ if (edge.kind === "calls" || edge.kind === "imports") {
3869
+ if (!visited.has(edge.source)) {
3870
+ queue.push({ id: edge.source, depth: depth + 1 });
3871
+ }
3872
+ }
3873
+ }
3874
+ }
3875
+ return Math.max(0, visited.size - 1);
3876
+ }
3877
+ function getRisk(blastRadius) {
3878
+ if (blastRadius > 20) return "HIGH";
3879
+ if (blastRadius >= 5) return "MEDIUM";
3880
+ return "LOW";
3881
+ }
3882
+ function computeCoverage(graph, scope) {
3883
+ const testFilePaths = /* @__PURE__ */ new Set();
3884
+ for (const node of graph.allNodes()) {
3885
+ if (isTestFile(node.filePath)) testFilePaths.add(node.filePath);
3886
+ }
3887
+ const nodesImportedByTests = /* @__PURE__ */ new Set();
3888
+ for (const edge of graph.findEdgesByKind("imports")) {
3889
+ const sourceNode = graph.getNode(edge.source);
3890
+ if (sourceNode && isTestFile(sourceNode.filePath)) {
3891
+ nodesImportedByTests.add(edge.target);
3892
+ }
3893
+ }
3894
+ const nodesWithTestedBy = /* @__PURE__ */ new Set();
3895
+ for (const edge of graph.findEdgesByKind("tested_by")) {
3896
+ nodesWithTestedBy.add(edge.source);
3897
+ }
3898
+ const baseNameToTestFiles = /* @__PURE__ */ new Map();
3899
+ for (const testPath of testFilePaths) {
3900
+ const base = path31.basename(testPath);
3901
+ const stripped = base.replace(/\.test\.[^.]+$/, "").replace(/\.spec\.[^.]+$/, "").replace(/_test\.[^.]+$/, "").replace(/_test$/, "");
3902
+ const existing = baseNameToTestFiles.get(stripped) ?? [];
3903
+ existing.push(testPath);
3904
+ baseNameToTestFiles.set(stripped, existing);
3905
+ }
3906
+ const exportedKinds = /* @__PURE__ */ new Set(["function", "method", "class"]);
3907
+ const results = [];
3908
+ for (const node of graph.allNodes()) {
3909
+ if (!exportedKinds.has(node.kind)) continue;
3910
+ if (node.exported !== true) continue;
3911
+ if (scope && !node.filePath.startsWith(scope)) continue;
3912
+ const testFiles = [];
3913
+ if (nodesWithTestedBy.has(node.id)) {
3914
+ for (const edge of graph.findEdgesFrom(node.id)) {
3915
+ if (edge.kind === "tested_by") {
3916
+ const testNode = graph.getNode(edge.target);
3917
+ if (testNode && !testFiles.includes(testNode.filePath)) {
3918
+ testFiles.push(testNode.filePath);
3919
+ }
3920
+ }
3921
+ }
3922
+ }
3923
+ if (nodesImportedByTests.has(node.id)) {
3924
+ for (const edge of graph.findEdgesTo(node.id)) {
3925
+ if (edge.kind === "imports") {
3926
+ const sourceNode = graph.getNode(edge.source);
3927
+ if (sourceNode && isTestFile(sourceNode.filePath) && !testFiles.includes(sourceNode.filePath)) {
3928
+ testFiles.push(sourceNode.filePath);
3929
+ }
3930
+ }
3931
+ }
3932
+ }
3933
+ const nodeBase = path31.basename(node.filePath).replace(/\.[^.]+$/, "");
3934
+ const matchingTestFiles = baseNameToTestFiles.get(nodeBase) ?? [];
3935
+ for (const tf of matchingTestFiles) {
3936
+ if (!testFiles.includes(tf)) testFiles.push(tf);
3937
+ }
3938
+ const tested = testFiles.length > 0;
3939
+ const blastRadius = computeBlastRadius(graph, node.id);
3940
+ const risk = getRisk(blastRadius);
3941
+ results.push({
3942
+ nodeId: node.id,
3943
+ name: node.name,
3944
+ filePath: node.filePath,
3945
+ exported: true,
3946
+ tested,
3947
+ testFiles,
3948
+ blastRadius,
3949
+ risk
3950
+ });
3951
+ }
3952
+ const totalExported = results.length;
3953
+ const testedExported = results.filter((r) => r.tested).length;
3954
+ const coveragePct = totalExported === 0 ? 100 : Math.round(testedExported / totalExported * 100);
3955
+ const untestedByRisk = results.filter((r) => !r.tested).sort((a, b) => b.blastRadius - a.blastRadius);
3956
+ return { totalExported, testedExported, coveragePct, untestedByRisk };
3957
+ }
3958
+ var init_test_coverage = __esm({
3959
+ "src/analysis/test-coverage.ts"() {
3960
+ }
3961
+ });
3962
+
3963
+ // src/security/secret-scanner.ts
3964
+ var secret_scanner_exports = {};
3965
+ __export(secret_scanner_exports, {
3966
+ SecretScanner: () => SecretScanner
3967
+ });
3968
+ function shannonEntropy(s) {
3969
+ const freq = /* @__PURE__ */ new Map();
3970
+ for (const c of s) freq.set(c, (freq.get(c) ?? 0) + 1);
3971
+ let entropy = 0;
3972
+ for (const count of freq.values()) {
3973
+ const p = count / s.length;
3974
+ entropy -= p * Math.log2(p);
3975
+ }
3976
+ return entropy;
3977
+ }
3978
+ function isTestFile2(filePath) {
3979
+ return filePath.includes(".test.") || filePath.includes(".spec.") || filePath.includes("fixtures/") || filePath.includes("mocks/");
3980
+ }
3981
+ var ENV_VAR_RE, SENSITIVE_NAME_RE, VALUE_PATTERNS, SecretScanner;
3982
+ var init_secret_scanner = __esm({
3983
+ "src/security/secret-scanner.ts"() {
3984
+ ENV_VAR_RE = /^process\.env\./;
3985
+ SENSITIVE_NAME_RE = /_SECRET$|_PASSWORD$|_TOKEN$|_KEY$|_API_KEY$/i;
3986
+ VALUE_PATTERNS = [
3987
+ [/sk-[A-Za-z0-9]{6,}/, "openai-api-key", "HIGH"],
3988
+ [/pk_live_[A-Za-z0-9]{20,}/, "stripe-key", "HIGH"],
3989
+ [/AKIA[0-9A-Z]{16}|aws.access.key/i, "aws-access-key", "HIGH"],
3990
+ [/xoxb-[0-9]{11}-[0-9]{11}-[A-Za-z0-9]{24}/, "slack-token", "HIGH"],
3991
+ [/postgres:\/\/[^@]+:[^@]+@/, "db-url-with-credentials", "HIGH"],
3992
+ [/mysql:\/\/[^@]+:[^@]+@/, "db-url-with-credentials", "HIGH"],
3993
+ [/-----BEGIN RSA PRIVATE KEY-----/, "rsa-private-key", "HIGH"]
3994
+ ];
3995
+ SecretScanner = class {
3996
+ scan(graph, options) {
3997
+ const findings = [];
3998
+ const includeTests = options?.includeTestFiles ?? false;
3999
+ const scope = options?.scope;
4000
+ const ignorePatterns = [...options?.ignorePatterns ?? []];
4001
+ if (options?.workspaceRoot) {
4002
+ try {
4003
+ const raw = fs24.readFileSync(path31.join(options.workspaceRoot, ".codeintelignore"), "utf-8");
4004
+ for (const line of raw.split("\n")) {
4005
+ const trimmed = line.trim();
4006
+ if (trimmed && !trimmed.startsWith("#")) ignorePatterns.push(trimmed);
4007
+ }
4008
+ } catch {
4009
+ }
4010
+ }
4011
+ for (const node of graph.allNodes()) {
4012
+ const filePath = node.filePath;
4013
+ if (scope && !filePath.startsWith(scope)) continue;
4014
+ if (!includeTests && isTestFile2(filePath)) continue;
4015
+ if (ignorePatterns.length > 0 && ignorePatterns.some((p) => filePath.includes(p))) continue;
4016
+ const meta = node.metadata;
4017
+ const rawValue = meta?.value ?? meta?.literalValue;
4018
+ if (typeof rawValue !== "string" || rawValue.trim() === "") continue;
4019
+ const value = rawValue.trim();
4020
+ if (ENV_VAR_RE.test(value)) continue;
4021
+ let matched = false;
4022
+ for (const [re, label, severity] of VALUE_PATTERNS) {
4023
+ if (re.test(value)) {
4024
+ node.metadata = {
4025
+ ...node.metadata ?? {},
4026
+ security: { secretRisk: true, secretPattern: label }
4027
+ };
4028
+ findings.push({
4029
+ file: filePath,
4030
+ line: node.startLine,
4031
+ symbol: node.name,
4032
+ pattern: label,
4033
+ severity
4034
+ });
4035
+ matched = true;
4036
+ break;
4037
+ }
4038
+ }
4039
+ if (matched) continue;
4040
+ if (SENSITIVE_NAME_RE.test(node.name)) {
4041
+ node.metadata = {
4042
+ ...node.metadata ?? {},
4043
+ security: { secretRisk: true, secretPattern: "sensitive-name-with-value" }
4044
+ };
4045
+ findings.push({
4046
+ file: filePath,
4047
+ line: node.startLine,
4048
+ symbol: node.name,
4049
+ pattern: "sensitive-name-with-value",
4050
+ severity: "MEDIUM"
4051
+ });
4052
+ continue;
4053
+ }
4054
+ if (SENSITIVE_NAME_RE.test(node.name) && value.length > 20 && shannonEntropy(value) > 4.5) {
4055
+ node.metadata = {
4056
+ ...node.metadata ?? {},
4057
+ security: { secretRisk: true, secretPattern: "high-entropy-string" }
4058
+ };
4059
+ findings.push({
4060
+ file: filePath,
4061
+ line: node.startLine,
4062
+ symbol: node.name,
4063
+ pattern: "high-entropy-string",
4064
+ severity: "MEDIUM"
4065
+ });
4066
+ }
4067
+ }
4068
+ return findings;
4069
+ }
4070
+ };
4071
+ }
4072
+ });
4073
+
4074
+ // src/security/vulnerability-detector.ts
4075
+ var vulnerability_detector_exports = {};
4076
+ __export(vulnerability_detector_exports, {
4077
+ VulnerabilityDetector: () => VulnerabilityDetector
4078
+ });
4079
+ function hasCallers(graph, nodeId) {
4080
+ for (const edge of graph.findEdgesTo(nodeId)) {
4081
+ if (edge.kind === "calls") return true;
4082
+ }
4083
+ return false;
4084
+ }
4085
+ var CWE, SQL_PATTERN, XSS_PATTERN, SSRF_PATTERN, PATH_PATTERN, CMD_PATTERN, VulnerabilityDetector;
4086
+ var init_vulnerability_detector = __esm({
4087
+ "src/security/vulnerability-detector.ts"() {
4088
+ CWE = {
4089
+ SQL_INJECTION: "CWE-89",
4090
+ XSS: "CWE-79",
4091
+ SSRF: "CWE-918",
4092
+ PATH_TRAVERSAL: "CWE-22",
4093
+ COMMAND_INJECTION: "CWE-78"
4094
+ };
4095
+ SQL_PATTERN = /(db|database|connection|knex|sequelize|pool)\.(query|execute|raw)/i;
4096
+ XSS_PATTERN = /innerHTML|outerHTML|document\.write|insertAdjacentHTML/i;
4097
+ SSRF_PATTERN = /^(fetch|axios|http\.request|got)$/i;
4098
+ PATH_PATTERN = /^(fs\.readFile|fs\.writeFile|path\.join|createReadStream)$/i;
4099
+ CMD_PATTERN = /^(exec|execSync|spawn|eval)$/i;
4100
+ VulnerabilityDetector = class {
4101
+ detect(graph, options) {
4102
+ const findings = [];
4103
+ const scope = options?.scope;
4104
+ const types = options?.types ? new Set(options.types) : null;
4105
+ const want = (t) => !types || types.has(t);
4106
+ const nodes = [...graph.allNodes()];
4107
+ for (const node of nodes) {
4108
+ if (node.kind === "vulnerability") continue;
4109
+ const filePath = node.filePath;
4110
+ if (scope && !filePath.startsWith(scope)) continue;
4111
+ const nodeName = node.name;
4112
+ const meta = node.metadata;
4113
+ if (want("SQL_INJECTION") && SQL_PATTERN.test(nodeName)) {
4114
+ const hasDynamic = meta?.hasStringConcatenation === true || hasCallers(graph, node.id);
4115
+ if (hasDynamic) {
4116
+ findings.push({
4117
+ type: "SQL_INJECTION",
4118
+ severity: "MEDIUM",
4119
+ file: filePath,
4120
+ line: node.startLine,
4121
+ symbol: nodeName,
4122
+ description: `Potential SQL injection: ${nodeName} may execute unsanitized user input`,
4123
+ cweId: CWE["SQL_INJECTION"]
4124
+ });
4125
+ this._tagNode(graph, node.id, "SQL_INJECTION");
4126
+ }
4127
+ }
4128
+ if (want("XSS") && XSS_PATTERN.test(nodeName)) {
4129
+ findings.push({
4130
+ type: "XSS",
4131
+ severity: "HIGH",
4132
+ file: filePath,
4133
+ line: node.startLine,
4134
+ symbol: nodeName,
4135
+ description: `Potential XSS: ${nodeName} writes to DOM sink`,
4136
+ cweId: CWE["XSS"]
4137
+ });
4138
+ this._tagNode(graph, node.id, "XSS");
4139
+ }
4140
+ if (want("XSS")) {
4141
+ for (const edge of graph.findEdgesFrom(node.id)) {
4142
+ if (edge.kind === "calls") {
4143
+ const callee = graph.getNode(edge.target);
4144
+ if (callee && XSS_PATTERN.test(callee.name)) {
4145
+ findings.push({
4146
+ type: "XSS",
4147
+ severity: "HIGH",
4148
+ file: filePath,
4149
+ line: node.startLine,
4150
+ symbol: node.name,
4151
+ description: `Potential XSS: ${node.name} calls ${callee.name}`,
4152
+ cweId: CWE["XSS"]
4153
+ });
4154
+ this._tagNode(graph, node.id, "XSS");
4155
+ }
4156
+ }
4157
+ }
4158
+ }
4159
+ if (want("SSRF") && SSRF_PATTERN.test(nodeName)) {
4160
+ const isDynamic = meta?.dynamicUrl === true;
4161
+ if (isDynamic) {
4162
+ findings.push({
4163
+ type: "SSRF",
4164
+ severity: "HIGH",
4165
+ file: filePath,
4166
+ line: node.startLine,
4167
+ symbol: nodeName,
4168
+ description: `Potential SSRF: ${nodeName} uses a dynamic URL`,
4169
+ cweId: CWE["SSRF"]
4170
+ });
4171
+ this._tagNode(graph, node.id, "SSRF");
4172
+ }
4173
+ }
4174
+ if (want("PATH_TRAVERSAL") && PATH_PATTERN.test(nodeName)) {
4175
+ const isDynamic = meta?.dynamicPath === true;
4176
+ if (isDynamic) {
4177
+ findings.push({
4178
+ type: "PATH_TRAVERSAL",
4179
+ severity: "HIGH",
4180
+ file: filePath,
4181
+ line: node.startLine,
4182
+ symbol: nodeName,
4183
+ description: `Potential path traversal: ${nodeName} uses a dynamic path`,
4184
+ cweId: CWE["PATH_TRAVERSAL"]
4185
+ });
4186
+ this._tagNode(graph, node.id, "PATH_TRAVERSAL");
4187
+ }
4188
+ }
4189
+ if (want("COMMAND_INJECTION") && CMD_PATTERN.test(nodeName)) {
4190
+ const isDynamic = meta?.dynamicArgs === true;
4191
+ if (isDynamic) {
4192
+ findings.push({
4193
+ type: "COMMAND_INJECTION",
4194
+ severity: "HIGH",
4195
+ file: filePath,
4196
+ line: node.startLine,
4197
+ symbol: nodeName,
4198
+ description: `Potential command injection: ${nodeName} uses dynamic arguments`,
4199
+ cweId: CWE["COMMAND_INJECTION"]
4200
+ });
4201
+ this._tagNode(graph, node.id, "COMMAND_INJECTION");
4202
+ }
4203
+ }
4204
+ }
4205
+ const seen = /* @__PURE__ */ new Set();
4206
+ return findings.filter((f) => {
4207
+ const key = `${f.type}:${f.file}:${f.line}:${f.symbol}`;
4208
+ if (seen.has(key)) return false;
4209
+ seen.add(key);
4210
+ return true;
4211
+ });
4212
+ }
4213
+ _tagNode(graph, nodeId, vulnType) {
4214
+ const node = graph.getNode(nodeId);
4215
+ if (!node) return;
4216
+ const meta = node.metadata ?? {};
4217
+ node.metadata = {
4218
+ ...meta,
4219
+ security: {
4220
+ ...meta["security"] ?? {},
4221
+ vulnerability: vulnType
4222
+ }
4223
+ };
4224
+ const vulnNodeId = `vuln:${vulnType}:${nodeId}`;
4225
+ if (!graph.getNode(vulnNodeId)) {
4226
+ graph.addNode({
4227
+ id: vulnNodeId,
4228
+ kind: "vulnerability",
4229
+ name: `${vulnType}:${node.name}`,
4230
+ filePath: node.filePath,
4231
+ startLine: node.startLine,
4232
+ metadata: { cweId: CWE[vulnType], vulnType }
4233
+ });
4234
+ }
4235
+ const edgeId = `has_vulnerability:${nodeId}:${vulnNodeId}`;
4236
+ if (!graph.getEdge(edgeId)) {
4237
+ graph.addEdge({
4238
+ id: edgeId,
4239
+ source: nodeId,
4240
+ target: vulnNodeId,
4241
+ kind: "has_vulnerability"
4242
+ });
4243
+ }
4244
+ }
4245
+ };
4246
+ }
4247
+ });
4248
+
3705
4249
  // src/errors/codes.ts
3706
4250
  var ErrorCodes, AppError;
3707
4251
  var init_codes = __esm({
@@ -3735,10 +4279,10 @@ var init_codes = __esm({
3735
4279
  }
3736
4280
  });
3737
4281
  function secureMkdir(dir) {
3738
- fs23.mkdirSync(dir, { recursive: true, mode: SECURE_DIR_MODE });
4282
+ fs24.mkdirSync(dir, { recursive: true, mode: SECURE_DIR_MODE });
3739
4283
  if (process.platform !== "win32") {
3740
4284
  try {
3741
- fs23.chmodSync(dir, SECURE_DIR_MODE);
4285
+ fs24.chmodSync(dir, SECURE_DIR_MODE);
3742
4286
  } catch {
3743
4287
  }
3744
4288
  }
@@ -3746,17 +4290,17 @@ function secureMkdir(dir) {
3746
4290
  function secureChmodFile(file) {
3747
4291
  if (process.platform === "win32") return;
3748
4292
  try {
3749
- fs23.chmodSync(file, SECURE_FILE_MODE);
4293
+ fs24.chmodSync(file, SECURE_FILE_MODE);
3750
4294
  } catch {
3751
4295
  }
3752
4296
  }
3753
4297
  function tightenDbFiles(dir) {
3754
4298
  if (process.platform === "win32") return;
3755
- if (!fs23.existsSync(dir)) return;
3756
- for (const name of fs23.readdirSync(dir)) {
4299
+ if (!fs24.existsSync(dir)) return;
4300
+ for (const name of fs24.readdirSync(dir)) {
3757
4301
  if (name.endsWith(".db") || name.endsWith(".db-wal") || name.endsWith(".db-shm")) {
3758
4302
  try {
3759
- fs23.chmodSync(path29.join(dir, name), SECURE_FILE_MODE);
4303
+ fs24.chmodSync(path31.join(dir, name), SECURE_FILE_MODE);
3760
4304
  } catch {
3761
4305
  }
3762
4306
  }
@@ -3770,7 +4314,7 @@ var init_fs_secure = __esm({
3770
4314
  }
3771
4315
  });
3772
4316
  function getUsersDBPath() {
3773
- return process.env["CODE_INTEL_USERS_DB_PATH"] ?? path29.join(os12.homedir(), ".code-intel", "users.db");
4317
+ return process.env["CODE_INTEL_USERS_DB_PATH"] ?? path31.join(os12.homedir(), ".code-intel", "users.db");
3774
4318
  }
3775
4319
  function getOrCreateUsersDB() {
3776
4320
  if (!_usersDB) {
@@ -3786,7 +4330,7 @@ var init_users_db = __esm({
3786
4330
  UsersDB = class {
3787
4331
  db;
3788
4332
  constructor(dbPath) {
3789
- const dir = path29.dirname(dbPath);
4333
+ const dir = path31.dirname(dbPath);
3790
4334
  secureMkdir(dir);
3791
4335
  this.db = new Database3(dbPath);
3792
4336
  this.db.pragma("journal_mode = WAL");
@@ -4063,7 +4607,7 @@ function getScryptN() {
4063
4607
  return Number.isInteger(v) && v >= 1024 ? v : 1 << 14;
4064
4608
  }
4065
4609
  function getSecretsPath() {
4066
- return process.env["CODE_INTEL_SECRETS_PATH"] ?? path29.join(os12.homedir(), ".code-intel", ".secrets");
4610
+ return process.env["CODE_INTEL_SECRETS_PATH"] ?? path31.join(os12.homedir(), ".code-intel", ".secrets");
4067
4611
  }
4068
4612
  function getMasterPassword() {
4069
4613
  const fromEnv = process.env["CODE_INTEL_SECRET_KEY"];
@@ -4094,8 +4638,8 @@ function decryptSecrets(encrypted) {
4094
4638
  return JSON.parse(plaintext.toString("utf8"));
4095
4639
  }
4096
4640
  function loadSecrets(secretsPath = getSecretsPath()) {
4097
- if (!fs23.existsSync(secretsPath)) return {};
4098
- const blob = fs23.readFileSync(secretsPath);
4641
+ if (!fs24.existsSync(secretsPath)) return {};
4642
+ const blob = fs24.readFileSync(secretsPath);
4099
4643
  return decryptSecrets(blob);
4100
4644
  }
4101
4645
  function getSecret(key, secretsPath = getSecretsPath()) {
@@ -4662,7 +5206,7 @@ init_shared();
4662
5206
  init_shared();
4663
5207
  init_typescript();
4664
5208
  function resolveRelative(rawPath, fromFile, workspace) {
4665
- const fromDir = path29.dirname(fromFile);
5209
+ const fromDir = path31.dirname(fromFile);
4666
5210
  const cleaned = rawPath.replace(/['"]/g, "");
4667
5211
  const extensions = [".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.js"];
4668
5212
  const resolved = workspace.resolve(fromDir, cleaned);
@@ -4714,7 +5258,7 @@ var pythonModule = {
4714
5258
  resolveImport(rawPath, fromFile, workspace) {
4715
5259
  const cleaned = rawPath.replace(/['"]/g, "");
4716
5260
  const parts = cleaned.split(".");
4717
- const fromDir = path29.dirname(fromFile);
5261
+ const fromDir = path31.dirname(fromFile);
4718
5262
  const relPath = parts.join("/");
4719
5263
  for (const suffix of ["/__init__.py", ".py"]) {
4720
5264
  const r = workspace.resolve(fromDir, relPath + suffix);
@@ -4793,7 +5337,7 @@ var cModule = {
4793
5337
  inheritanceStrategy: "none",
4794
5338
  resolveImport(rawPath, fromFile, workspace) {
4795
5339
  const cleaned = rawPath.replace(/[<>"']/g, "");
4796
- const fromDir = path29.dirname(fromFile);
5340
+ const fromDir = path31.dirname(fromFile);
4797
5341
  return workspace.resolve(fromDir, cleaned);
4798
5342
  },
4799
5343
  isExported(_node) {
@@ -4816,7 +5360,7 @@ var cppModule = {
4816
5360
  inheritanceStrategy: "depth-first",
4817
5361
  resolveImport(rawPath, fromFile, workspace) {
4818
5362
  const cleaned = rawPath.replace(/[<>"']/g, "");
4819
- const fromDir = path29.dirname(fromFile);
5363
+ const fromDir = path31.dirname(fromFile);
4820
5364
  return workspace.resolve(fromDir, cleaned);
4821
5365
  },
4822
5366
  isExported(_node) {
@@ -4978,7 +5522,7 @@ var dartModule = {
4978
5522
  const pkg = cleaned.replace("package:", "");
4979
5523
  return workspace.findByPackage(pkg);
4980
5524
  }
4981
- const fromDir = path29.dirname(fromFile);
5525
+ const fromDir = path31.dirname(fromFile);
4982
5526
  return workspace.resolve(fromDir, cleaned);
4983
5527
  },
4984
5528
  isExported(node) {
@@ -5333,25 +5877,25 @@ function validateDAG(phases) {
5333
5877
  const visiting = /* @__PURE__ */ new Set();
5334
5878
  const visited = /* @__PURE__ */ new Set();
5335
5879
  const phaseMap = new Map(phases.map((p) => [p.name, p]));
5336
- function dfs(name, path30) {
5880
+ function dfs(name, path32) {
5337
5881
  if (visiting.has(name)) {
5338
- const cycleStart = path30.indexOf(name);
5339
- const cycle = path30.slice(cycleStart).concat(name);
5882
+ const cycleStart = path32.indexOf(name);
5883
+ const cycle = path32.slice(cycleStart).concat(name);
5340
5884
  errors.push({ type: "cycle", message: `Cycle detected: ${cycle.join(" \u2192 ")}` });
5341
5885
  return true;
5342
5886
  }
5343
5887
  if (visited.has(name)) return false;
5344
5888
  visiting.add(name);
5345
- path30.push(name);
5889
+ path32.push(name);
5346
5890
  const phase = phaseMap.get(name);
5347
5891
  if (phase) {
5348
5892
  for (const dep of phase.dependencies) {
5349
- if (dfs(dep, path30)) return true;
5893
+ if (dfs(dep, path32)) return true;
5350
5894
  }
5351
5895
  }
5352
5896
  visiting.delete(name);
5353
5897
  visited.add(name);
5354
- path30.pop();
5898
+ path32.pop();
5355
5899
  return false;
5356
5900
  }
5357
5901
  for (const phase of phases) {
@@ -5564,7 +6108,7 @@ var IGNORED_DIRS = /* @__PURE__ */ new Set([
5564
6108
  ]);
5565
6109
  function loadIgnorePatterns(workspaceRoot) {
5566
6110
  try {
5567
- const raw = fs23.readFileSync(path29.join(workspaceRoot, ".codeintelignore"), "utf-8");
6111
+ const raw = fs24.readFileSync(path31.join(workspaceRoot, ".codeintelignore"), "utf-8");
5568
6112
  const extras = /* @__PURE__ */ new Set();
5569
6113
  for (const line of raw.split("\n")) {
5570
6114
  const trimmed = line.trim();
@@ -5588,7 +6132,7 @@ var scanPhase = {
5588
6132
  function walk(dir) {
5589
6133
  let entries;
5590
6134
  try {
5591
- entries = fs23.readdirSync(dir, { withFileTypes: true });
6135
+ entries = fs24.readdirSync(dir, { withFileTypes: true });
5592
6136
  } catch {
5593
6137
  return;
5594
6138
  }
@@ -5597,15 +6141,15 @@ var scanPhase = {
5597
6141
  if (entry.name.startsWith(".")) continue;
5598
6142
  if (IGNORED_DIRS.has(entry.name)) continue;
5599
6143
  if (extraIgnore.has(entry.name)) continue;
5600
- walk(path29.join(dir, entry.name));
6144
+ walk(path31.join(dir, entry.name));
5601
6145
  } else if (entry.isFile()) {
5602
6146
  const name = entry.name;
5603
6147
  if (IGNORED_FILE_SUFFIXES.some((s) => name.endsWith(s))) continue;
5604
- const ext = path29.extname(name);
6148
+ const ext = path31.extname(name);
5605
6149
  if (!extensions.has(ext)) continue;
5606
- const fullPath = path29.join(dir, name);
6150
+ const fullPath = path31.join(dir, name);
5607
6151
  try {
5608
- const stat = fs23.statSync(fullPath);
6152
+ const stat = fs24.statSync(fullPath);
5609
6153
  if (stat.size > MAX_FILE_SIZE_BYTES) continue;
5610
6154
  } catch {
5611
6155
  continue;
@@ -5632,20 +6176,20 @@ var structurePhase = {
5632
6176
  const dirs = /* @__PURE__ */ new Set();
5633
6177
  let structDone = 0;
5634
6178
  for (const filePath of context2.filePaths) {
5635
- const relativePath = path29.relative(context2.workspaceRoot, filePath);
6179
+ const relativePath = path31.relative(context2.workspaceRoot, filePath);
5636
6180
  const lang = detectLanguage(filePath);
5637
6181
  context2.graph.addNode({
5638
6182
  id: generateNodeId("file", relativePath, relativePath),
5639
6183
  kind: "file",
5640
- name: path29.basename(filePath),
6184
+ name: path31.basename(filePath),
5641
6185
  filePath: relativePath,
5642
6186
  metadata: lang ? { language: lang } : void 0
5643
6187
  });
5644
- let dir = path29.dirname(relativePath);
6188
+ let dir = path31.dirname(relativePath);
5645
6189
  while (dir && dir !== "." && dir !== "") {
5646
6190
  if (dirs.has(dir)) break;
5647
6191
  dirs.add(dir);
5648
- dir = path29.dirname(dir);
6192
+ dir = path31.dirname(dir);
5649
6193
  }
5650
6194
  structDone++;
5651
6195
  context2.onPhaseProgress?.("structure", structDone, context2.filePaths.length);
@@ -5654,7 +6198,7 @@ var structurePhase = {
5654
6198
  context2.graph.addNode({
5655
6199
  id: generateNodeId("directory", dir, dir),
5656
6200
  kind: "directory",
5657
- name: path29.basename(dir),
6201
+ name: path31.basename(dir),
5658
6202
  filePath: dir
5659
6203
  });
5660
6204
  }
@@ -5741,9 +6285,9 @@ var flowPhase = {
5741
6285
  for (const node of graph.allNodes()) {
5742
6286
  if (!["function", "method"].includes(node.kind)) continue;
5743
6287
  let score = 0;
5744
- const hasCallers = calledNodes.has(node.id);
6288
+ const hasCallers2 = calledNodes.has(node.id);
5745
6289
  const outCalls = [...graph.findEdgesFrom(node.id)].filter((e) => e.kind === "calls");
5746
- if (!hasCallers && outCalls.length > 0) score += 10;
6290
+ if (!hasCallers2 && outCalls.length > 0) score += 10;
5747
6291
  if (node.exported) score += 5;
5748
6292
  if (/^(main|handle|init|start|run|execute|process|serve|listen|bootstrap)/.test(node.name)) score += 3;
5749
6293
  if (node.filePath.includes("test") || node.filePath.includes("spec") || node.filePath.includes("__test")) score -= 20;
@@ -5765,22 +6309,22 @@ var flowPhase = {
5765
6309
  const queue = [{ nodeId: ep.id, path: [ep.id] }];
5766
6310
  const visited = /* @__PURE__ */ new Set();
5767
6311
  while (queue.length > 0 && flowCount < maxFlows) {
5768
- const { nodeId, path: path30 } = queue.shift();
5769
- if (path30.length > maxDepth) continue;
6312
+ const { nodeId, path: path32 } = queue.shift();
6313
+ if (path32.length > maxDepth) continue;
5770
6314
  const callEdges = [...graph.findEdgesFrom(nodeId)].filter((e) => e.kind === "calls").slice(0, maxBranching);
5771
- if (callEdges.length === 0 && path30.length >= 3) {
6315
+ if (callEdges.length === 0 && path32.length >= 3) {
5772
6316
  const flowId = generateNodeId("flow", ep.filePath, `flow-${flowCount}`);
5773
6317
  graph.addNode({
5774
6318
  id: flowId,
5775
6319
  kind: "flow",
5776
6320
  name: `${ep.name} flow ${flowCount}`,
5777
6321
  filePath: ep.filePath,
5778
- metadata: { steps: path30, entryPoint: ep.name }
6322
+ metadata: { steps: path32, entryPoint: ep.name }
5779
6323
  });
5780
- for (let i = 0; i < path30.length; i++) {
6324
+ for (let i = 0; i < path32.length; i++) {
5781
6325
  graph.addEdge({
5782
- id: generateEdgeId(path30[i], flowId, `step_of_${i}`),
5783
- source: path30[i],
6326
+ id: generateEdgeId(path32[i], flowId, `step_of_${i}`),
6327
+ source: path32[i],
5784
6328
  target: flowId,
5785
6329
  kind: "step_of",
5786
6330
  weight: 1,
@@ -5793,7 +6337,7 @@ var flowPhase = {
5793
6337
  for (const edge of callEdges) {
5794
6338
  if (visited.has(edge.target)) continue;
5795
6339
  visited.add(edge.target);
5796
- queue.push({ nodeId: edge.target, path: [...path30, edge.target] });
6340
+ queue.push({ nodeId: edge.target, path: [...path32, edge.target] });
5797
6341
  }
5798
6342
  }
5799
6343
  }
@@ -5811,7 +6355,7 @@ var LLMGovernanceLogger = class {
5811
6355
  }
5812
6356
  /** Path to the JSONL log file. */
5813
6357
  getLogPath() {
5814
- return process.env["CODE_INTEL_GOVERNANCE_LOG_PATH"] ?? path29.join(os12.homedir(), ".code-intel", "llm-governance.jsonl");
6358
+ return process.env["CODE_INTEL_GOVERNANCE_LOG_PATH"] ?? path31.join(os12.homedir(), ".code-intel", "llm-governance.jsonl");
5815
6359
  }
5816
6360
  /**
5817
6361
  * Append an entry to the governance log.
@@ -5827,8 +6371,8 @@ var LLMGovernanceLogger = class {
5827
6371
  ...entry
5828
6372
  };
5829
6373
  const logPath = this.getLogPath();
5830
- fs23.mkdirSync(path29.dirname(logPath), { recursive: true });
5831
- fs23.appendFileSync(logPath, JSON.stringify(full) + "\n", "utf-8");
6374
+ fs24.mkdirSync(path31.dirname(logPath), { recursive: true });
6375
+ fs24.appendFileSync(logPath, JSON.stringify(full) + "\n", "utf-8");
5832
6376
  } catch {
5833
6377
  }
5834
6378
  }
@@ -5838,7 +6382,7 @@ var LLMGovernanceLogger = class {
5838
6382
  */
5839
6383
  readLog(limit = 100) {
5840
6384
  try {
5841
- const raw = fs23.readFileSync(this.getLogPath(), "utf-8");
6385
+ const raw = fs24.readFileSync(this.getLogPath(), "utf-8");
5842
6386
  const lines = raw.split("\n").filter((l) => l.trim().length > 0).slice(-limit);
5843
6387
  return lines.map((l) => JSON.parse(l));
5844
6388
  } catch {
@@ -5982,17 +6526,17 @@ function traceFlow(entryId, graph, maxDepth = 10, maxBranching = 4) {
5982
6526
  const queue = [{ nodeId: entryId, path: [entryId] }];
5983
6527
  const visited = /* @__PURE__ */ new Set();
5984
6528
  while (queue.length > 0 && flows.length < maxFlows) {
5985
- const { nodeId, path: path30 } = queue.shift();
5986
- if (path30.length > maxDepth) continue;
6529
+ const { nodeId, path: path32 } = queue.shift();
6530
+ if (path32.length > maxDepth) continue;
5987
6531
  const callEdges = [...graph.findEdgesFrom(nodeId)].filter((e) => e.kind === "calls").slice(0, maxBranching);
5988
- if (callEdges.length === 0 && path30.length >= 3) {
5989
- flows.push({ entryPointId: entryId, steps: [...path30] });
6532
+ if (callEdges.length === 0 && path32.length >= 3) {
6533
+ flows.push({ entryPointId: entryId, steps: [...path32] });
5990
6534
  continue;
5991
6535
  }
5992
6536
  for (const edge of callEdges) {
5993
6537
  if (visited.has(edge.target)) continue;
5994
6538
  visited.add(edge.target);
5995
- queue.push({ nodeId: edge.target, path: [...path30, edge.target] });
6539
+ queue.push({ nodeId: edge.target, path: [...path32, edge.target] });
5996
6540
  }
5997
6541
  }
5998
6542
  }
@@ -6226,7 +6770,7 @@ init_embedder();
6226
6770
  async function hybridSearch(graph, query, limit, options = {}) {
6227
6771
  const { vectorDbPath, bm25Limit = 50, vectorLimit = 50 } = options;
6228
6772
  const bm25Promise = Promise.resolve(textSearch(graph, query, bm25Limit));
6229
- const hasVectorDb = Boolean(vectorDbPath && fs23.existsSync(vectorDbPath));
6773
+ const hasVectorDb = Boolean(vectorDbPath && fs24.existsSync(vectorDbPath));
6230
6774
  if (!hasVectorDb) {
6231
6775
  const bm25Results2 = await bm25Promise;
6232
6776
  return {
@@ -6283,7 +6827,7 @@ var DbManager = class {
6283
6827
  this.dbPath = dbPath;
6284
6828
  }
6285
6829
  async init() {
6286
- fs23.mkdirSync(path29.dirname(this.dbPath), { recursive: true });
6830
+ fs24.mkdirSync(path31.dirname(this.dbPath), { recursive: true });
6287
6831
  this.db = new Database(this.dbPath);
6288
6832
  await this.db.init();
6289
6833
  this.conn = new Connection(this.db);
@@ -6373,7 +6917,7 @@ function getCreateEdgeTableDDL() {
6373
6917
  )`];
6374
6918
  }
6375
6919
  function writeNodeCSVs(graph, outputDir) {
6376
- fs23.mkdirSync(outputDir, { recursive: true });
6920
+ fs24.mkdirSync(outputDir, { recursive: true });
6377
6921
  const header = "id,name,file_path,start_line,end_line,exported,content,metadata\n";
6378
6922
  const tableBuffers = /* @__PURE__ */ new Map();
6379
6923
  const tableFilePaths = /* @__PURE__ */ new Map();
@@ -6381,7 +6925,7 @@ function writeNodeCSVs(graph, outputDir) {
6381
6925
  const table = NODE_TABLE_MAP[node.kind];
6382
6926
  if (!tableBuffers.has(table)) {
6383
6927
  tableBuffers.set(table, [header]);
6384
- tableFilePaths.set(table, path29.join(outputDir, `${table}.csv`));
6928
+ tableFilePaths.set(table, path31.join(outputDir, `${table}.csv`));
6385
6929
  }
6386
6930
  tableBuffers.get(table).push(
6387
6931
  csvRow([
@@ -6401,12 +6945,12 @@ function writeNodeCSVs(graph, outputDir) {
6401
6945
  );
6402
6946
  }
6403
6947
  for (const [table, lines] of tableBuffers) {
6404
- fs23.writeFileSync(tableFilePaths.get(table), lines.join(""), "utf-8");
6948
+ fs24.writeFileSync(tableFilePaths.get(table), lines.join(""), "utf-8");
6405
6949
  }
6406
6950
  return tableFilePaths;
6407
6951
  }
6408
6952
  function writeEdgeCSV(graph, outputDir) {
6409
- fs23.mkdirSync(outputDir, { recursive: true });
6953
+ fs24.mkdirSync(outputDir, { recursive: true });
6410
6954
  const header = "from_id,to_id,kind,weight,label\n";
6411
6955
  const groups = /* @__PURE__ */ new Map();
6412
6956
  for (const edge of graph.allEdges()) {
@@ -6417,7 +6961,7 @@ function writeEdgeCSV(graph, outputDir) {
6417
6961
  const toTable = NODE_TABLE_MAP[targetNode.kind];
6418
6962
  const key = `${fromTable}->${toTable}`;
6419
6963
  if (!groups.has(key)) {
6420
- const filePath = path29.join(outputDir, `edges_${fromTable}_${toTable}.csv`);
6964
+ const filePath = path31.join(outputDir, `edges_${fromTable}_${toTable}.csv`);
6421
6965
  groups.set(key, { lines: [header], from: fromTable, to: toTable, filePath });
6422
6966
  }
6423
6967
  groups.get(key).lines.push(
@@ -6432,7 +6976,7 @@ function writeEdgeCSV(graph, outputDir) {
6432
6976
  }
6433
6977
  const result = [];
6434
6978
  for (const group of groups.values()) {
6435
- fs23.writeFileSync(group.filePath, group.lines.join(""), "utf-8");
6979
+ fs24.writeFileSync(group.filePath, group.lines.join(""), "utf-8");
6436
6980
  result.push({ fromTable: group.from, toTable: group.to, filePath: group.filePath });
6437
6981
  }
6438
6982
  return result;
@@ -6460,7 +7004,7 @@ async function loadGraphToDB(graph, dbManager) {
6460
7004
  } catch {
6461
7005
  }
6462
7006
  }
6463
- const tmpDir = fs23.mkdtempSync(path29.join(os12.tmpdir(), "code-intel-csv-"));
7007
+ const tmpDir = fs24.mkdtempSync(path31.join(os12.tmpdir(), "code-intel-csv-"));
6464
7008
  try {
6465
7009
  const nodeTableFiles = writeNodeCSVs(graph, tmpDir);
6466
7010
  const edgeGroups = writeEdgeCSV(graph, tmpDir);
@@ -6479,8 +7023,8 @@ async function loadGraphToDB(graph, dbManager) {
6479
7023
  }
6480
7024
  let nodeCount = 0;
6481
7025
  for (const [table, csvPath] of nodeTableFiles) {
6482
- if (!fs23.existsSync(csvPath)) continue;
6483
- const stat = fs23.statSync(csvPath);
7026
+ if (!fs24.existsSync(csvPath)) continue;
7027
+ const stat = fs24.statSync(csvPath);
6484
7028
  if (stat.size < 50) continue;
6485
7029
  try {
6486
7030
  await dbManager.execute(
@@ -6493,8 +7037,8 @@ async function loadGraphToDB(graph, dbManager) {
6493
7037
  }
6494
7038
  let edgeCount = 0;
6495
7039
  for (const group of edgeGroups) {
6496
- if (!fs23.existsSync(group.filePath)) continue;
6497
- const stat = fs23.statSync(group.filePath);
7040
+ if (!fs24.existsSync(group.filePath)) continue;
7041
+ const stat = fs24.statSync(group.filePath);
6498
7042
  if (stat.size < 50) continue;
6499
7043
  try {
6500
7044
  await dbManager.execute(
@@ -6508,7 +7052,7 @@ async function loadGraphToDB(graph, dbManager) {
6508
7052
  return { nodeCount, edgeCount };
6509
7053
  } finally {
6510
7054
  try {
6511
- fs23.rmSync(tmpDir, { recursive: true, force: true });
7055
+ fs24.rmSync(tmpDir, { recursive: true, force: true });
6512
7056
  } catch {
6513
7057
  }
6514
7058
  }
@@ -6560,19 +7104,19 @@ function buildNodeProps(node) {
6560
7104
  function escCypher(s) {
6561
7105
  return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "");
6562
7106
  }
6563
- var GLOBAL_DIR = path29.join(os12.homedir(), ".code-intel");
6564
- var REPOS_FILE = path29.join(GLOBAL_DIR, "repos.json");
7107
+ var GLOBAL_DIR = path31.join(os12.homedir(), ".code-intel");
7108
+ var REPOS_FILE = path31.join(GLOBAL_DIR, "repos.json");
6565
7109
  function loadRegistry() {
6566
7110
  try {
6567
- const data = fs23.readFileSync(REPOS_FILE, "utf-8");
7111
+ const data = fs24.readFileSync(REPOS_FILE, "utf-8");
6568
7112
  return JSON.parse(data);
6569
7113
  } catch {
6570
7114
  return [];
6571
7115
  }
6572
7116
  }
6573
7117
  function saveRegistry(entries) {
6574
- fs23.mkdirSync(GLOBAL_DIR, { recursive: true });
6575
- fs23.writeFileSync(REPOS_FILE, JSON.stringify(entries, null, 2));
7118
+ fs24.mkdirSync(GLOBAL_DIR, { recursive: true });
7119
+ fs24.writeFileSync(REPOS_FILE, JSON.stringify(entries, null, 2));
6576
7120
  }
6577
7121
  function upsertRepo(entry) {
6578
7122
  const entries = loadRegistry();
@@ -6589,23 +7133,23 @@ function removeRepo(repoPath) {
6589
7133
  saveRegistry(entries);
6590
7134
  }
6591
7135
  function saveMetadata(repoDir, metadata) {
6592
- const metaDir = path29.join(repoDir, ".code-intel");
6593
- fs23.mkdirSync(metaDir, { recursive: true });
6594
- fs23.writeFileSync(path29.join(metaDir, "meta.json"), JSON.stringify(metadata, null, 2));
7136
+ const metaDir = path31.join(repoDir, ".code-intel");
7137
+ fs24.mkdirSync(metaDir, { recursive: true });
7138
+ fs24.writeFileSync(path31.join(metaDir, "meta.json"), JSON.stringify(metadata, null, 2));
6595
7139
  }
6596
7140
  function loadMetadata(repoDir) {
6597
7141
  try {
6598
- const data = fs23.readFileSync(path29.join(repoDir, ".code-intel", "meta.json"), "utf-8");
7142
+ const data = fs24.readFileSync(path31.join(repoDir, ".code-intel", "meta.json"), "utf-8");
6599
7143
  return JSON.parse(data);
6600
7144
  } catch {
6601
7145
  return null;
6602
7146
  }
6603
7147
  }
6604
7148
  function getDbPath(repoDir) {
6605
- return path29.join(repoDir, ".code-intel", "graph.db");
7149
+ return path31.join(repoDir, ".code-intel", "graph.db");
6606
7150
  }
6607
7151
  function getVectorDbPath(repoDir) {
6608
- return path29.join(repoDir, ".code-intel", "vector.db");
7152
+ return path31.join(repoDir, ".code-intel", "vector.db");
6609
7153
  }
6610
7154
 
6611
7155
  // src/mcp-server/server.ts
@@ -6689,12 +7233,12 @@ function scanForFiles(root, matcher, maxDepth = 2) {
6689
7233
  if (depth > maxDepth) return;
6690
7234
  let entries;
6691
7235
  try {
6692
- entries = fs23.readdirSync(dir, { withFileTypes: true });
7236
+ entries = fs24.readdirSync(dir, { withFileTypes: true });
6693
7237
  } catch {
6694
7238
  return;
6695
7239
  }
6696
7240
  for (const entry of entries) {
6697
- const full = path29.join(dir, entry.name);
7241
+ const full = path31.join(dir, entry.name);
6698
7242
  if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
6699
7243
  walk(full, depth + 1);
6700
7244
  } else if (entry.isFile() && matcher(entry.name)) {
@@ -6717,8 +7261,8 @@ var OPENAPI_FILENAMES = /* @__PURE__ */ new Set([
6717
7261
  ]);
6718
7262
  var HTTP_METHODS = ["get", "post", "put", "patch", "delete", "head", "options"];
6719
7263
  function tryParseFile(filePath) {
6720
- const ext = path29.extname(filePath).toLowerCase();
6721
- const content = fs23.readFileSync(filePath, "utf-8");
7264
+ const ext = path31.extname(filePath).toLowerCase();
7265
+ const content = fs24.readFileSync(filePath, "utf-8");
6722
7266
  if (ext === ".json") {
6723
7267
  try {
6724
7268
  return JSON.parse(content);
@@ -6772,7 +7316,7 @@ async function parseGraphQLContracts(repoRoot) {
6772
7316
  const files = scanForFiles(repoRoot, (name) => name.endsWith(".graphql") || name.endsWith(".gql"));
6773
7317
  const contracts = [];
6774
7318
  for (const filePath of files) {
6775
- const content = fs23.readFileSync(filePath, "utf-8");
7319
+ const content = fs24.readFileSync(filePath, "utf-8");
6776
7320
  const typeRegex = /type\s+(\w+)\s*\{([^}]+)\}/g;
6777
7321
  let match;
6778
7322
  while ((match = typeRegex.exec(content)) !== null) {
@@ -6803,7 +7347,7 @@ async function parseProtoContracts(repoRoot) {
6803
7347
  const files = scanForFiles(repoRoot, (name) => name.endsWith(".proto"));
6804
7348
  const contracts = [];
6805
7349
  for (const filePath of files) {
6806
- const content = fs23.readFileSync(filePath, "utf-8");
7350
+ const content = fs24.readFileSync(filePath, "utf-8");
6807
7351
  const serviceRegex = /service\s+(\w+)\s*\{([^}]+)\}/g;
6808
7352
  let serviceMatch;
6809
7353
  while ((serviceMatch = serviceRegex.exec(content)) !== null) {
@@ -7008,8 +7552,8 @@ async function syncGroup(group) {
7008
7552
  logger_default.warn(` \u26A0 Registry entry "${member.registryName}" not found \u2014 skipping ${member.groupPath}`);
7009
7553
  continue;
7010
7554
  }
7011
- const dbPath = path29.join(regEntry.path, ".code-intel", "graph.db");
7012
- if (!fs23.existsSync(dbPath)) {
7555
+ const dbPath = path31.join(regEntry.path, ".code-intel", "graph.db");
7556
+ if (!fs24.existsSync(dbPath)) {
7013
7557
  logger_default.warn(` \u26A0 No index at ${dbPath} \u2014 run \`code-intel analyze ${regEntry.path}\` first`);
7014
7558
  continue;
7015
7559
  }
@@ -7082,8 +7626,8 @@ async function queryGroup(group, query, limit = 20) {
7082
7626
  for (const member of group.members) {
7083
7627
  const regEntry = registry.find((r) => r.name === member.registryName);
7084
7628
  if (!regEntry) continue;
7085
- const dbPath = path29.join(regEntry.path, ".code-intel", "graph.db");
7086
- if (!fs23.existsSync(dbPath)) continue;
7629
+ const dbPath = path31.join(regEntry.path, ".code-intel", "graph.db");
7630
+ if (!fs24.existsSync(dbPath)) continue;
7087
7631
  const graph = createKnowledgeGraph();
7088
7632
  const db = new DbManager(dbPath);
7089
7633
  try {
@@ -7583,22 +8127,22 @@ function suggestTests(graph, symbolName) {
7583
8127
  const callPaths = [];
7584
8128
  const pathQueue = [{ id: targetId, path: [symbolName], depth: 0 }];
7585
8129
  while (pathQueue.length > 0 && callPaths.length < 5) {
7586
- const { id, path: path30, depth } = pathQueue.shift();
7587
- let hasCallers = false;
8130
+ const { id, path: path32, depth } = pathQueue.shift();
8131
+ let hasCallers2 = false;
7588
8132
  for (const edge of graph.findEdgesTo(id)) {
7589
8133
  if (edge.kind !== "calls") continue;
7590
8134
  const callerNode = graph.getNode(edge.source);
7591
8135
  if (!callerNode) continue;
7592
- hasCallers = true;
7593
- const newPath = [callerNode.name, ...path30];
8136
+ hasCallers2 = true;
8137
+ const newPath = [callerNode.name, ...path32];
7594
8138
  if (depth + 1 >= 3 || callPaths.length >= 5) {
7595
8139
  if (callPaths.length < 5) callPaths.push(newPath);
7596
8140
  continue;
7597
8141
  }
7598
8142
  pathQueue.push({ id: edge.source, path: newPath, depth: depth + 1 });
7599
8143
  }
7600
- if (!hasCallers && path30.length > 1) {
7601
- callPaths.push(path30);
8144
+ if (!hasCallers2 && path32.length > 1) {
8145
+ callPaths.push(path32);
7602
8146
  }
7603
8147
  }
7604
8148
  if (callPaths.length === 0) {
@@ -8073,6 +8617,70 @@ function createMcpServer(graph, repoName, workspaceRoot) {
8073
8617
  },
8074
8618
  required: ["cluster"]
8075
8619
  }
8620
+ },
8621
+ {
8622
+ name: "deprecated_usage",
8623
+ description: "Find usages of deprecated APIs in the codebase",
8624
+ inputSchema: {
8625
+ type: "object",
8626
+ properties: {
8627
+ scope: { type: "string", description: "Directory scope filter" },
8628
+ ..._tokenProp
8629
+ }
8630
+ }
8631
+ },
8632
+ {
8633
+ name: "complexity_hotspots",
8634
+ description: "Ranked list of functions/methods by cyclomatic complexity. Useful for identifying refactoring candidates.",
8635
+ inputSchema: {
8636
+ type: "object",
8637
+ properties: {
8638
+ scope: { type: "string", description: "Limit to a file path prefix (optional)" },
8639
+ limit: { type: "number", description: "Maximum number of results (default: 20)" },
8640
+ ..._tokenProp
8641
+ }
8642
+ }
8643
+ },
8644
+ {
8645
+ name: "coverage_gaps",
8646
+ description: "Find exported symbols with no test coverage, ranked by blast radius. Useful for prioritizing test writing.",
8647
+ inputSchema: {
8648
+ type: "object",
8649
+ properties: {
8650
+ scope: { type: "string", description: "Limit to a file path prefix (optional)" },
8651
+ limit: { type: "number", description: "Maximum number of untested results to return (default: 20)" },
8652
+ ..._tokenProp
8653
+ }
8654
+ }
8655
+ },
8656
+ {
8657
+ name: "secrets",
8658
+ description: "Scan the knowledge graph for hardcoded secrets: API keys, passwords, tokens, private keys, high-entropy strings",
8659
+ inputSchema: {
8660
+ type: "object",
8661
+ properties: {
8662
+ scope: { type: "string", description: "Limit scan to files under this path prefix" },
8663
+ includeTestFiles: { type: "boolean", description: "Include test/spec/fixture files (default: false)" },
8664
+ ..._tokenProp
8665
+ }
8666
+ }
8667
+ },
8668
+ {
8669
+ name: "vulnerability_scan",
8670
+ description: "Scan the knowledge graph for OWASP vulnerabilities: SQL injection, XSS, SSRF, path traversal, command injection",
8671
+ inputSchema: {
8672
+ type: "object",
8673
+ properties: {
8674
+ scope: { type: "string", description: "Limit scan to files under this path prefix" },
8675
+ types: {
8676
+ type: "array",
8677
+ items: { type: "string", enum: ["SQL_INJECTION", "XSS", "SSRF", "PATH_TRAVERSAL", "COMMAND_INJECTION"] },
8678
+ description: "Vulnerability types to detect (default: all)"
8679
+ },
8680
+ severity: { type: "string", description: "Minimum severity to report: HIGH|MEDIUM|LOW (default: LOW)" },
8681
+ ..._tokenProp
8682
+ }
8683
+ }
8076
8684
  }
8077
8685
  ]
8078
8686
  }));
@@ -8496,7 +9104,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
8496
9104
  for (const { filePath: changedFile, changedLines } of changedFiles) {
8497
9105
  for (const node of graph.allNodes()) {
8498
9106
  if (!node.filePath) continue;
8499
- const normNode = node.filePath.replace(repoRoot + "/", "").replace(repoRoot + path29.sep, "");
9107
+ const normNode = node.filePath.replace(repoRoot + "/", "").replace(repoRoot + path31.sep, "");
8500
9108
  const normChanged = changedFile.replace(/^a\/|^b\//, "");
8501
9109
  if (!normNode.endsWith(normChanged) && !normChanged.endsWith(normNode)) continue;
8502
9110
  if (node.startLine !== void 0 && node.endLine !== void 0) {
@@ -8769,6 +9377,63 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
8769
9377
  const result = summarizeCluster(graph, cluster);
8770
9378
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
8771
9379
  }
9380
+ case "deprecated_usage": {
9381
+ const scope = a.scope;
9382
+ const { DeprecatedDetector: DeprecatedDetector2 } = await Promise.resolve().then(() => (init_deprecated_detector(), deprecated_detector_exports));
9383
+ const detector = new DeprecatedDetector2();
9384
+ detector.tagDeprecated(graph);
9385
+ const findings = detector.detect(graph, scope);
9386
+ return { content: [{ type: "text", text: JSON.stringify({ findings, total: findings.length }, null, 2) }] };
9387
+ }
9388
+ // ── complexity_hotspots ────────────────────────────────────────────────
9389
+ case "complexity_hotspots": {
9390
+ const { computeComplexity: computeComplexity2 } = await Promise.resolve().then(() => (init_complexity(), complexity_exports));
9391
+ const scope = a.scope;
9392
+ const limit = typeof a.limit === "number" ? a.limit : 20;
9393
+ const hotspots = computeComplexity2(graph, scope).slice(0, limit);
9394
+ return { content: [{ type: "text", text: JSON.stringify({ hotspots, total: hotspots.length }, null, 2) }] };
9395
+ }
9396
+ // ── coverage_gaps ──────────────────────────────────────────────────────
9397
+ case "coverage_gaps": {
9398
+ const { computeCoverage: computeCoverage2 } = await Promise.resolve().then(() => (init_test_coverage(), test_coverage_exports));
9399
+ const scope = a.scope;
9400
+ const limit = typeof a.limit === "number" ? a.limit : 20;
9401
+ const summary = computeCoverage2(graph, scope);
9402
+ const untestedByRisk = summary.untestedByRisk.slice(0, limit);
9403
+ return {
9404
+ content: [{
9405
+ type: "text",
9406
+ text: JSON.stringify({
9407
+ untestedByRisk,
9408
+ coveragePct: summary.coveragePct,
9409
+ totalExported: summary.totalExported,
9410
+ testedExported: summary.testedExported
9411
+ }, null, 2)
9412
+ }]
9413
+ };
9414
+ }
9415
+ // ── secrets ────────────────────────────────────────────────────────────
9416
+ case "secrets": {
9417
+ const { SecretScanner: SecretScanner2 } = await Promise.resolve().then(() => (init_secret_scanner(), secret_scanner_exports));
9418
+ const scanner = new SecretScanner2();
9419
+ const scope = a.scope;
9420
+ const includeTestFiles = a.includeTestFiles ?? false;
9421
+ const findings = scanner.scan(graph, { scope, includeTestFiles });
9422
+ return { content: [{ type: "text", text: JSON.stringify({ findings, total: findings.length }, null, 2) }] };
9423
+ }
9424
+ // ── vulnerability_scan ─────────────────────────────────────────────────
9425
+ case "vulnerability_scan": {
9426
+ const { VulnerabilityDetector: VulnerabilityDetector2 } = await Promise.resolve().then(() => (init_vulnerability_detector(), vulnerability_detector_exports));
9427
+ const detector = new VulnerabilityDetector2();
9428
+ const scope = a.scope;
9429
+ const types = a.types;
9430
+ const minSev = (a.severity ?? "LOW").toUpperCase();
9431
+ const sevRank = { HIGH: 3, MEDIUM: 2, LOW: 1 };
9432
+ const minRank = sevRank[minSev] ?? 1;
9433
+ let findings = detector.detect(graph, { scope, types });
9434
+ findings = findings.filter((f) => (sevRank[f.severity] ?? 1) >= minRank);
9435
+ return { content: [{ type: "text", text: JSON.stringify({ findings, total: findings.length }, null, 2) }] };
9436
+ }
8772
9437
  default:
8773
9438
  return { content: [{ type: "text", text: `Unknown tool: ${name}` }] };
8774
9439
  }
@@ -8862,7 +9527,7 @@ var STUCK_THRESHOLD_MINUTES = 30;
8862
9527
  var JobsDB = class {
8863
9528
  db;
8864
9529
  constructor(dbPath) {
8865
- fs23.mkdirSync(path29.dirname(dbPath), { recursive: true });
9530
+ fs24.mkdirSync(path31.dirname(dbPath), { recursive: true });
8866
9531
  this.db = new Database3(dbPath);
8867
9532
  this.db.pragma("journal_mode = WAL");
8868
9533
  this.db.pragma("foreign_keys = ON");
@@ -9004,7 +9669,7 @@ var JobsDB = class {
9004
9669
  }
9005
9670
  };
9006
9671
  function getJobsDBPath() {
9007
- return path29.join(os12.homedir(), ".code-intel", "jobs.db");
9672
+ return path31.join(os12.homedir(), ".code-intel", "jobs.db");
9008
9673
  }
9009
9674
  var _jobsDB = null;
9010
9675
  function getOrCreateJobsDB() {
@@ -9096,7 +9761,7 @@ var BACKUP_VERSION = "1.0";
9096
9761
  var ALGORITHM = "aes-256-gcm";
9097
9762
  var IV_LENGTH = 16;
9098
9763
  function getBackupDir() {
9099
- return path29.join(os12.homedir(), ".code-intel", "backups");
9764
+ return path31.join(os12.homedir(), ".code-intel", "backups");
9100
9765
  }
9101
9766
  function getBackupKey() {
9102
9767
  const keyHex = process.env["CODE_INTEL_BACKUP_KEY"];
@@ -9127,30 +9792,30 @@ var BackupService = class {
9127
9792
  constructor(backupDir) {
9128
9793
  this.backupDir = backupDir ?? getBackupDir();
9129
9794
  this.key = getBackupKey();
9130
- fs23.mkdirSync(this.backupDir, { recursive: true });
9795
+ fs24.mkdirSync(this.backupDir, { recursive: true });
9131
9796
  }
9132
9797
  /**
9133
9798
  * Create a backup for a repository.
9134
9799
  * Returns the backup entry.
9135
9800
  */
9136
9801
  createBackup(repoPath) {
9137
- const codeIntelDir = path29.join(repoPath, ".code-intel");
9802
+ const codeIntelDir = path31.join(repoPath, ".code-intel");
9138
9803
  const id = v4();
9139
9804
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
9140
9805
  const filesToBackup = [];
9141
9806
  const candidates = ["graph.db", "vector.db", "meta.json"];
9142
9807
  for (const f of candidates) {
9143
- const fp = path29.join(codeIntelDir, f);
9144
- if (fs23.existsSync(fp)) {
9808
+ const fp = path31.join(codeIntelDir, f);
9809
+ if (fs24.existsSync(fp)) {
9145
9810
  filesToBackup.push({ name: f, localPath: fp });
9146
9811
  }
9147
9812
  }
9148
- const registryPath = path29.join(os12.homedir(), ".code-intel", "registry.json");
9149
- if (fs23.existsSync(registryPath)) {
9813
+ const registryPath = path31.join(os12.homedir(), ".code-intel", "registry.json");
9814
+ if (fs24.existsSync(registryPath)) {
9150
9815
  filesToBackup.push({ name: "registry.json", localPath: registryPath });
9151
9816
  }
9152
- const usersDbPath = path29.join(os12.homedir(), ".code-intel", "users.db");
9153
- if (fs23.existsSync(usersDbPath)) {
9817
+ const usersDbPath = path31.join(os12.homedir(), ".code-intel", "users.db");
9818
+ if (fs24.existsSync(usersDbPath)) {
9154
9819
  filesToBackup.push({ name: "users.db", localPath: usersDbPath });
9155
9820
  }
9156
9821
  if (filesToBackup.length === 0) {
@@ -9161,7 +9826,7 @@ var BackupService = class {
9161
9826
  createdAt,
9162
9827
  version: BACKUP_VERSION,
9163
9828
  files: filesToBackup.map((f) => {
9164
- const data = fs23.readFileSync(f.localPath);
9829
+ const data = fs24.readFileSync(f.localPath);
9165
9830
  return {
9166
9831
  name: f.name,
9167
9832
  sha256: crypto5.createHash("sha256").update(data).digest("hex"),
@@ -9175,7 +9840,7 @@ var BackupService = class {
9175
9840
  manifestLenBuf.writeUInt32BE(manifestBuf.length, 0);
9176
9841
  parts.push(manifestLenBuf, manifestBuf);
9177
9842
  for (const f of filesToBackup) {
9178
- const data = fs23.readFileSync(f.localPath);
9843
+ const data = fs24.readFileSync(f.localPath);
9179
9844
  const nameBuf = Buffer.from(f.name, "utf-8");
9180
9845
  const nameLenBuf = Buffer.alloc(2);
9181
9846
  nameLenBuf.writeUInt16BE(nameBuf.length, 0);
@@ -9186,8 +9851,8 @@ var BackupService = class {
9186
9851
  const plaintext = Buffer.concat(parts);
9187
9852
  const encrypted = encryptBuffer(plaintext, this.key);
9188
9853
  const backupFileName = `backup-${id}.cib`;
9189
- const backupPath = path29.join(this.backupDir, backupFileName);
9190
- fs23.writeFileSync(backupPath, encrypted);
9854
+ const backupPath = path31.join(this.backupDir, backupFileName);
9855
+ fs24.writeFileSync(backupPath, encrypted);
9191
9856
  const entry = {
9192
9857
  id,
9193
9858
  createdAt,
@@ -9214,9 +9879,9 @@ var BackupService = class {
9214
9879
  async uploadToS3(entry) {
9215
9880
  const cfg = getS3Config();
9216
9881
  if (!cfg) throw new Error("S3 not configured. Set CODE_INTEL_BACKUP_S3_BUCKET, CODE_INTEL_BACKUP_S3_ACCESS_KEY_ID, CODE_INTEL_BACKUP_S3_SECRET_ACCESS_KEY.");
9217
- const fileName = path29.basename(entry.path);
9882
+ const fileName = path31.basename(entry.path);
9218
9883
  const s3Key = `${cfg.prefix}${fileName}`;
9219
- const body = fs23.readFileSync(entry.path);
9884
+ const body = fs24.readFileSync(entry.path);
9220
9885
  const result = await s3Request({ method: "PUT", cfg, key: s3Key, body });
9221
9886
  if (result.statusCode < 200 || result.statusCode >= 300) {
9222
9887
  throw new Error(`S3 upload failed (HTTP ${result.statusCode}): ${result.body.slice(0, 200)}`);
@@ -9233,8 +9898,8 @@ var BackupService = class {
9233
9898
  if (result.statusCode < 200 || result.statusCode >= 300) {
9234
9899
  throw new Error(`S3 download failed (HTTP ${result.statusCode}): ${result.body.slice(0, 200)}`);
9235
9900
  }
9236
- fs23.mkdirSync(path29.dirname(destPath), { recursive: true });
9237
- fs23.writeFileSync(destPath, Buffer.from(result.body, "binary"));
9901
+ fs24.mkdirSync(path31.dirname(destPath), { recursive: true });
9902
+ fs24.writeFileSync(destPath, Buffer.from(result.body, "binary"));
9238
9903
  }
9239
9904
  /**
9240
9905
  * List backup objects in S3 with the configured prefix.
@@ -9280,10 +9945,10 @@ var BackupService = class {
9280
9945
  if (!entry) {
9281
9946
  throw new Error(`Backup "${backupId}" not found.`);
9282
9947
  }
9283
- if (!fs23.existsSync(entry.path)) {
9948
+ if (!fs24.existsSync(entry.path)) {
9284
9949
  throw new Error(`Backup file not found at: ${entry.path}`);
9285
9950
  }
9286
- const encrypted = fs23.readFileSync(entry.path);
9951
+ const encrypted = fs24.readFileSync(entry.path);
9287
9952
  let plaintext;
9288
9953
  try {
9289
9954
  plaintext = decryptBuffer(encrypted, this.key);
@@ -9297,8 +9962,8 @@ var BackupService = class {
9297
9962
  offset += manifestLen;
9298
9963
  const manifest = JSON.parse(manifestStr);
9299
9964
  const restoreBase = targetRepoPath ?? entry.repoPath;
9300
- const codeIntelDir = path29.join(restoreBase, ".code-intel");
9301
- fs23.mkdirSync(codeIntelDir, { recursive: true });
9965
+ const codeIntelDir = path31.join(restoreBase, ".code-intel");
9966
+ fs24.mkdirSync(codeIntelDir, { recursive: true });
9302
9967
  for (const fileEntry of manifest.files) {
9303
9968
  const nameLen = plaintext.readUInt16BE(offset);
9304
9969
  offset += 2;
@@ -9315,18 +9980,18 @@ var BackupService = class {
9315
9980
  }
9316
9981
  let destPath;
9317
9982
  if (name === "registry.json" || name === "users.db") {
9318
- destPath = path29.join(os12.homedir(), ".code-intel", name);
9983
+ destPath = path31.join(os12.homedir(), ".code-intel", name);
9319
9984
  } else {
9320
- destPath = path29.join(codeIntelDir, name);
9985
+ destPath = path31.join(codeIntelDir, name);
9321
9986
  }
9322
- fs23.writeFileSync(destPath, data);
9987
+ fs24.writeFileSync(destPath, data);
9323
9988
  }
9324
9989
  }
9325
9990
  /**
9326
9991
  * Apply retention policy: keep N daily, M weekly, L monthly backups.
9327
9992
  */
9328
9993
  applyRetention(options = { daily: 7, weekly: 4, monthly: 12 }) {
9329
- const entries = this._loadIndex().filter((e) => fs23.existsSync(e.path)).sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
9994
+ const entries = this._loadIndex().filter((e) => fs24.existsSync(e.path)).sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
9330
9995
  const keep = /* @__PURE__ */ new Set();
9331
9996
  const now = /* @__PURE__ */ new Date();
9332
9997
  const dailyCutoff = new Date(now);
@@ -9356,7 +10021,7 @@ var BackupService = class {
9356
10021
  for (const e of entries) {
9357
10022
  if (!keep.has(e.id)) {
9358
10023
  try {
9359
- fs23.unlinkSync(e.path);
10024
+ fs24.unlinkSync(e.path);
9360
10025
  deleted++;
9361
10026
  } catch {
9362
10027
  }
@@ -9368,17 +10033,17 @@ var BackupService = class {
9368
10033
  }
9369
10034
  // ── Index helpers ──────────────────────────────────────────────────────────
9370
10035
  _indexPath() {
9371
- return path29.join(this.backupDir, "index.json");
10036
+ return path31.join(this.backupDir, "index.json");
9372
10037
  }
9373
10038
  _loadIndex() {
9374
10039
  try {
9375
- return JSON.parse(fs23.readFileSync(this._indexPath(), "utf-8"));
10040
+ return JSON.parse(fs24.readFileSync(this._indexPath(), "utf-8"));
9376
10041
  } catch {
9377
10042
  return [];
9378
10043
  }
9379
10044
  }
9380
10045
  _saveIndex(entries) {
9381
- fs23.writeFileSync(this._indexPath(), JSON.stringify(entries, null, 2));
10046
+ fs24.writeFileSync(this._indexPath(), JSON.stringify(entries, null, 2));
9382
10047
  }
9383
10048
  _appendIndex(entry) {
9384
10049
  const entries = this._loadIndex();
@@ -10171,11 +10836,11 @@ var openApiSpec = {
10171
10836
  };
10172
10837
 
10173
10838
  // src/http/app.ts
10174
- var __dirname$1 = path29.dirname(fileURLToPath(import.meta.url));
10839
+ var __dirname$1 = path31.dirname(fileURLToPath(import.meta.url));
10175
10840
  var WEB_DIST = (() => {
10176
- const bundled = path29.resolve(__dirname$1, "..", "web");
10177
- if (fs23.existsSync(bundled)) return bundled;
10178
- return path29.resolve(__dirname$1, "..", "..", "..", "web", "dist");
10841
+ const bundled = path31.resolve(__dirname$1, "..", "web");
10842
+ if (fs24.existsSync(bundled)) return bundled;
10843
+ return path31.resolve(__dirname$1, "..", "..", "..", "web", "dist");
10179
10844
  })();
10180
10845
  function getAllowedOrigins() {
10181
10846
  const env = process.env["CODE_INTEL_CORS_ORIGINS"];
@@ -10706,8 +11371,8 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
10706
11371
  const registry = loadRegistry();
10707
11372
  const entry = registry.find((r) => r.name === requestedRepo || r.path === requestedRepo);
10708
11373
  if (!entry) return null;
10709
- const dbPath = path29.join(entry.path, ".code-intel", "graph.db");
10710
- if (!fs23.existsSync(dbPath)) return null;
11374
+ const dbPath = path31.join(entry.path, ".code-intel", "graph.db");
11375
+ if (!fs24.existsSync(dbPath)) return null;
10711
11376
  const repoGraph = createKnowledgeGraph();
10712
11377
  const db = new DbManager(dbPath);
10713
11378
  try {
@@ -10794,7 +11459,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
10794
11459
  return;
10795
11460
  }
10796
11461
  try {
10797
- const content = fs23.readFileSync(file_path, "utf-8");
11462
+ const content = fs24.readFileSync(file_path, "utf-8");
10798
11463
  res.json({ content });
10799
11464
  } catch {
10800
11465
  res.status(404).json({ error: { code: ErrorCodes.NOT_FOUND, message: "File not found" } });
@@ -11052,8 +11717,8 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11052
11717
  for (const member of group.members) {
11053
11718
  const regEntry = registry.find((r) => r.name === member.registryName);
11054
11719
  if (!regEntry) continue;
11055
- const dbPath = path29.join(regEntry.path, ".code-intel", "graph.db");
11056
- if (!fs23.existsSync(dbPath)) continue;
11720
+ const dbPath = path31.join(regEntry.path, ".code-intel", "graph.db");
11721
+ if (!fs24.existsSync(dbPath)) continue;
11057
11722
  const db = new DbManager(dbPath);
11058
11723
  try {
11059
11724
  await db.init();
@@ -11079,8 +11744,8 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11079
11744
  let nodeCount = 0;
11080
11745
  let edgeCount = 0;
11081
11746
  if (regEntry) {
11082
- const dbPath = path29.join(regEntry.path, ".code-intel", "graph.db");
11083
- if (fs23.existsSync(dbPath)) {
11747
+ const dbPath = path31.join(regEntry.path, ".code-intel", "graph.db");
11748
+ if (fs24.existsSync(dbPath)) {
11084
11749
  try {
11085
11750
  const db = new DbManager(dbPath);
11086
11751
  await db.init();
@@ -11129,14 +11794,14 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11129
11794
  });
11130
11795
  return;
11131
11796
  }
11132
- let rawResolved = path29.normalize(file);
11133
- if (!path29.isAbsolute(rawResolved) && workspaceRoot) {
11134
- rawResolved = path29.join(workspaceRoot, rawResolved);
11797
+ let rawResolved = path31.normalize(file);
11798
+ if (!path31.isAbsolute(rawResolved) && workspaceRoot) {
11799
+ rawResolved = path31.join(workspaceRoot, rawResolved);
11135
11800
  }
11136
- const resolvedFile = path29.resolve(rawResolved);
11801
+ const resolvedFile = path31.resolve(rawResolved);
11137
11802
  function isInsideDir(fileAbs, dir) {
11138
- const rel = path29.relative(path29.resolve(dir), fileAbs);
11139
- return !rel.startsWith("..") && !path29.isAbsolute(rel);
11803
+ const rel = path31.relative(path31.resolve(dir), fileAbs);
11804
+ return !rel.startsWith("..") && !path31.isAbsolute(rel);
11140
11805
  }
11141
11806
  if (workspaceRoot) {
11142
11807
  if (!isInsideDir(resolvedFile, workspaceRoot)) {
@@ -11173,7 +11838,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11173
11838
  }
11174
11839
  let fileContent;
11175
11840
  try {
11176
- fileContent = fs23.readFileSync(resolvedFile, "utf-8");
11841
+ fileContent = fs24.readFileSync(resolvedFile, "utf-8");
11177
11842
  } catch {
11178
11843
  res.status(404).json({
11179
11844
  error: {
@@ -11204,7 +11869,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11204
11869
  const contextStart = Math.max(1, startLine - 20);
11205
11870
  const contextEnd = Math.min(lines.length, endLine + 20);
11206
11871
  const content = lines.slice(contextStart - 1, contextEnd).join("\n");
11207
- const ext = path29.extname(resolvedFile).toLowerCase();
11872
+ const ext = path31.extname(resolvedFile).toLowerCase();
11208
11873
  const languageMap = {
11209
11874
  ".ts": "typescript",
11210
11875
  ".tsx": "typescript",
@@ -11339,10 +12004,10 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
11339
12004
  res.status(500).json({ error: { code: ErrorCodes.INTERNAL_ERROR, message: err instanceof Error ? err.message : String(err), requestId: req.requestId, timestamp: (/* @__PURE__ */ new Date()).toISOString() } });
11340
12005
  }
11341
12006
  });
11342
- if (fs23.existsSync(WEB_DIST)) {
12007
+ if (fs24.existsSync(WEB_DIST)) {
11343
12008
  app.use(express.static(WEB_DIST));
11344
12009
  app.get("/{*path}", (_req, res) => {
11345
- res.sendFile(path29.join(WEB_DIST, "index.html"));
12010
+ res.sendFile(path31.join(WEB_DIST, "index.html"));
11346
12011
  });
11347
12012
  }
11348
12013
  app.use("/admin", requireRole("admin"));