@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/cli/main.js CHANGED
@@ -7,8 +7,8 @@ import { SEMRESATTRS_DEPLOYMENT_ENVIRONMENT, SEMRESATTRS_SERVICE_NAME } from '@o
7
7
  import { SpanStatusCode, trace, context } from '@opentelemetry/api';
8
8
  import winston from 'winston';
9
9
  import DailyRotateFile from 'winston-daily-rotate-file';
10
- import fs33, { readFileSync, existsSync } from 'fs';
11
- import path33, { dirname, join } from 'path';
10
+ import fs34, { readFileSync, existsSync } from 'fs';
11
+ import path35, { dirname, join } from 'path';
12
12
  import os12 from 'os';
13
13
  import { Registry, collectDefaultMetrics, Counter, Histogram, Gauge } from 'prom-client';
14
14
  import { createRequire } from 'module';
@@ -326,7 +326,7 @@ var init_logger = __esm({
326
326
  };
327
327
  }
328
328
  /** Global log directory: ~/.code-intel/logs */
329
- static LOG_DIR = path33.join(os12.homedir(), ".code-intel", "logs");
329
+ static LOG_DIR = path35.join(os12.homedir(), ".code-intel", "logs");
330
330
  static getLogger() {
331
331
  if (!_Logger.instance) {
332
332
  const isProduction = process.env.NODE_ENV === "production";
@@ -335,12 +335,12 @@ var init_logger = __esm({
335
335
  transports.push(new winston.transports.Console());
336
336
  if (!isProduction) {
337
337
  try {
338
- if (!fs33.existsSync(_Logger.LOG_DIR)) {
339
- fs33.mkdirSync(_Logger.LOG_DIR, { recursive: true });
338
+ if (!fs34.existsSync(_Logger.LOG_DIR)) {
339
+ fs34.mkdirSync(_Logger.LOG_DIR, { recursive: true });
340
340
  }
341
341
  transports.push(
342
342
  new DailyRotateFile({
343
- filename: path33.join(_Logger.LOG_DIR, "%DATE%-code-intel.log"),
343
+ filename: path35.join(_Logger.LOG_DIR, "%DATE%-code-intel.log"),
344
344
  datePattern: "YYYY-MM-DD",
345
345
  maxSize: "20m",
346
346
  maxFiles: "14d"
@@ -540,25 +540,25 @@ function validateDAG(phases) {
540
540
  const visiting = /* @__PURE__ */ new Set();
541
541
  const visited = /* @__PURE__ */ new Set();
542
542
  const phaseMap = new Map(phases.map((p) => [p.name, p]));
543
- function dfs(name, path34) {
543
+ function dfs(name, path36) {
544
544
  if (visiting.has(name)) {
545
- const cycleStart = path34.indexOf(name);
546
- const cycle = path34.slice(cycleStart).concat(name);
545
+ const cycleStart = path36.indexOf(name);
546
+ const cycle = path36.slice(cycleStart).concat(name);
547
547
  errors.push({ type: "cycle", message: `Cycle detected: ${cycle.join(" \u2192 ")}` });
548
548
  return true;
549
549
  }
550
550
  if (visited.has(name)) return false;
551
551
  visiting.add(name);
552
- path34.push(name);
552
+ path36.push(name);
553
553
  const phase = phaseMap.get(name);
554
554
  if (phase) {
555
555
  for (const dep of phase.dependencies) {
556
- if (dfs(dep, path34)) return true;
556
+ if (dfs(dep, path36)) return true;
557
557
  }
558
558
  }
559
559
  visiting.delete(name);
560
560
  visited.add(name);
561
- path34.pop();
561
+ path36.pop();
562
562
  return false;
563
563
  }
564
564
  for (const phase of phases) {
@@ -818,11 +818,11 @@ var init_id_generator = __esm({
818
818
  }
819
819
  });
820
820
  function findBundledWasmDir() {
821
- const fileDir = path33.dirname(fileURLToPath(import.meta.url));
821
+ const fileDir = path35.dirname(fileURLToPath(import.meta.url));
822
822
  const candidates = [
823
- path33.join(fileDir, "wasm"),
823
+ path35.join(fileDir, "wasm"),
824
824
  // dist/index.js → dist/wasm/
825
- path33.join(fileDir, "../wasm")
825
+ path35.join(fileDir, "../wasm")
826
826
  // dist/cli/main.js → dist/wasm/
827
827
  ];
828
828
  for (const candidate of candidates) {
@@ -863,7 +863,7 @@ function wasmPath(lang) {
863
863
  }
864
864
  const bundled = BUNDLED_WASM_MAP[lang];
865
865
  if (bundled) {
866
- const bundledPath = path33.join(_bundledWasmDir, bundled);
866
+ const bundledPath = path35.join(_bundledWasmDir, bundled);
867
867
  if (existsSync(bundledPath)) return bundledPath;
868
868
  }
869
869
  return null;
@@ -876,14 +876,14 @@ async function initParser() {
876
876
  }
877
877
  async function getLanguage(lang) {
878
878
  if (languageCache.has(lang)) return languageCache.get(lang);
879
- const path34 = wasmPath(lang);
880
- if (!path34) {
879
+ const path36 = wasmPath(lang);
880
+ if (!path36) {
881
881
  languageCache.set(lang, null);
882
882
  return null;
883
883
  }
884
884
  try {
885
885
  await initParser();
886
- const language = await Language.load(path34);
886
+ const language = await Language.load(path36);
887
887
  languageCache.set(lang, language);
888
888
  return language;
889
889
  } catch {
@@ -2308,7 +2308,7 @@ var init_parse_phase = __esm({
2308
2308
  const batch = filePaths.slice(i, i + CONCURRENCY);
2309
2309
  await Promise.all(batch.map(async (filePath) => {
2310
2310
  try {
2311
- const source = await fs33.promises.readFile(filePath, "utf-8");
2311
+ const source = await fs34.promises.readFile(filePath, "utf-8");
2312
2312
  context2.fileCache.set(filePath, source);
2313
2313
  } catch {
2314
2314
  }
@@ -2321,14 +2321,14 @@ var init_parse_phase = __esm({
2321
2321
  const lang = detectLanguage(filePath);
2322
2322
  if (!lang) {
2323
2323
  if (context2.verbose) {
2324
- const relativePath2 = path33.relative(context2.workspaceRoot, filePath);
2324
+ const relativePath2 = path35.relative(context2.workspaceRoot, filePath);
2325
2325
  logger_default.info(` [parse] skipped (no parser): ${relativePath2}`);
2326
2326
  }
2327
2327
  continue;
2328
2328
  }
2329
2329
  const source = context2.fileCache.get(filePath);
2330
2330
  if (!source) continue;
2331
- const relativePath = path33.relative(context2.workspaceRoot, filePath);
2331
+ const relativePath = path35.relative(context2.workspaceRoot, filePath);
2332
2332
  const fileNodeId = generateNodeId("file", relativePath, relativePath);
2333
2333
  const fileNode = context2.graph.getNode(fileNodeId);
2334
2334
  if (fileNode) {
@@ -2574,11 +2574,11 @@ var init_resolve_phase = __esm({
2574
2574
  let heritageEdges = 0;
2575
2575
  const fileIndex = /* @__PURE__ */ new Map();
2576
2576
  for (const fp of filePaths) {
2577
- const rel = path33.relative(workspaceRoot, fp);
2577
+ const rel = path35.relative(workspaceRoot, fp);
2578
2578
  fileIndex.set(rel, fp);
2579
2579
  const noExt = rel.replace(/\.\w+$/, "");
2580
2580
  if (!fileIndex.has(noExt)) fileIndex.set(noExt, fp);
2581
- const base = path33.basename(rel, path33.extname(rel));
2581
+ const base = path35.basename(rel, path35.extname(rel));
2582
2582
  if (!fileIndex.has(base)) fileIndex.set(base, fp);
2583
2583
  }
2584
2584
  const symbolIndex = /* @__PURE__ */ new Map();
@@ -2609,7 +2609,7 @@ var init_resolve_phase = __esm({
2609
2609
  for (const filePath of filePaths) {
2610
2610
  const lang = detectLanguage(filePath);
2611
2611
  if (!lang) continue;
2612
- const relativePath = path33.relative(workspaceRoot, filePath);
2612
+ const relativePath = path35.relative(workspaceRoot, filePath);
2613
2613
  const fileNodeId = generateNodeId("file", relativePath, relativePath);
2614
2614
  const source = fileCache.get(filePath);
2615
2615
  if (!source) continue;
@@ -2622,13 +2622,13 @@ var init_resolve_phase = __esm({
2622
2622
  let resolvedRelPath = null;
2623
2623
  if (cleaned.startsWith(".")) {
2624
2624
  const cleanedNoJs = cleaned.replace(/\.(js|jsx)$/, "");
2625
- const fromDir = path33.dirname(relativePath);
2625
+ const fromDir = path35.dirname(relativePath);
2626
2626
  for (const ext of ["", ".ts", ".tsx", ".js", ".jsx", ".py", ".java", ".go", "/index.ts", "/index.js"]) {
2627
- const candidate = path33.join(fromDir, cleanedNoJs + ext);
2628
- const normalized = path33.normalize(candidate);
2627
+ const candidate = path35.join(fromDir, cleanedNoJs + ext);
2628
+ const normalized = path35.normalize(candidate);
2629
2629
  if (fileIndex.has(normalized)) {
2630
2630
  const absPath = fileIndex.get(normalized);
2631
- resolvedRelPath = path33.relative(workspaceRoot, absPath);
2631
+ resolvedRelPath = path35.relative(workspaceRoot, absPath);
2632
2632
  break;
2633
2633
  }
2634
2634
  }
@@ -3093,7 +3093,7 @@ var init_db_manager = __esm({
3093
3093
  this.dbPath = dbPath;
3094
3094
  }
3095
3095
  async init() {
3096
- fs33.mkdirSync(path33.dirname(this.dbPath), { recursive: true });
3096
+ fs34.mkdirSync(path35.dirname(this.dbPath), { recursive: true });
3097
3097
  this.db = new Database$1(this.dbPath);
3098
3098
  await this.db.init();
3099
3099
  this.conn = new Connection(this.db);
@@ -3190,7 +3190,7 @@ var init_schema = __esm({
3190
3190
  }
3191
3191
  });
3192
3192
  function writeNodeCSVs(graph, outputDir) {
3193
- fs33.mkdirSync(outputDir, { recursive: true });
3193
+ fs34.mkdirSync(outputDir, { recursive: true });
3194
3194
  const header = "id,name,file_path,start_line,end_line,exported,content,metadata\n";
3195
3195
  const tableBuffers = /* @__PURE__ */ new Map();
3196
3196
  const tableFilePaths = /* @__PURE__ */ new Map();
@@ -3198,7 +3198,7 @@ function writeNodeCSVs(graph, outputDir) {
3198
3198
  const table = NODE_TABLE_MAP[node.kind];
3199
3199
  if (!tableBuffers.has(table)) {
3200
3200
  tableBuffers.set(table, [header]);
3201
- tableFilePaths.set(table, path33.join(outputDir, `${table}.csv`));
3201
+ tableFilePaths.set(table, path35.join(outputDir, `${table}.csv`));
3202
3202
  }
3203
3203
  tableBuffers.get(table).push(
3204
3204
  csvRow([
@@ -3218,12 +3218,12 @@ function writeNodeCSVs(graph, outputDir) {
3218
3218
  );
3219
3219
  }
3220
3220
  for (const [table, lines] of tableBuffers) {
3221
- fs33.writeFileSync(tableFilePaths.get(table), lines.join(""), "utf-8");
3221
+ fs34.writeFileSync(tableFilePaths.get(table), lines.join(""), "utf-8");
3222
3222
  }
3223
3223
  return tableFilePaths;
3224
3224
  }
3225
3225
  function writeEdgeCSV(graph, outputDir) {
3226
- fs33.mkdirSync(outputDir, { recursive: true });
3226
+ fs34.mkdirSync(outputDir, { recursive: true });
3227
3227
  const header = "from_id,to_id,kind,weight,label\n";
3228
3228
  const groups = /* @__PURE__ */ new Map();
3229
3229
  for (const edge of graph.allEdges()) {
@@ -3234,7 +3234,7 @@ function writeEdgeCSV(graph, outputDir) {
3234
3234
  const toTable = NODE_TABLE_MAP[targetNode.kind];
3235
3235
  const key = `${fromTable}->${toTable}`;
3236
3236
  if (!groups.has(key)) {
3237
- const filePath = path33.join(outputDir, `edges_${fromTable}_${toTable}.csv`);
3237
+ const filePath = path35.join(outputDir, `edges_${fromTable}_${toTable}.csv`);
3238
3238
  groups.set(key, { lines: [header], from: fromTable, to: toTable, filePath });
3239
3239
  }
3240
3240
  groups.get(key).lines.push(
@@ -3249,7 +3249,7 @@ function writeEdgeCSV(graph, outputDir) {
3249
3249
  }
3250
3250
  const result = [];
3251
3251
  for (const group of groups.values()) {
3252
- fs33.writeFileSync(group.filePath, group.lines.join(""), "utf-8");
3252
+ fs34.writeFileSync(group.filePath, group.lines.join(""), "utf-8");
3253
3253
  result.push({ fromTable: group.from, toTable: group.to, filePath: group.filePath });
3254
3254
  }
3255
3255
  return result;
@@ -3292,7 +3292,7 @@ async function loadGraphToDB(graph, dbManager) {
3292
3292
  } catch {
3293
3293
  }
3294
3294
  }
3295
- const tmpDir = fs33.mkdtempSync(path33.join(os12.tmpdir(), "code-intel-csv-"));
3295
+ const tmpDir = fs34.mkdtempSync(path35.join(os12.tmpdir(), "code-intel-csv-"));
3296
3296
  try {
3297
3297
  const nodeTableFiles = writeNodeCSVs(graph, tmpDir);
3298
3298
  const edgeGroups = writeEdgeCSV(graph, tmpDir);
@@ -3311,8 +3311,8 @@ async function loadGraphToDB(graph, dbManager) {
3311
3311
  }
3312
3312
  let nodeCount = 0;
3313
3313
  for (const [table, csvPath] of nodeTableFiles) {
3314
- if (!fs33.existsSync(csvPath)) continue;
3315
- const stat = fs33.statSync(csvPath);
3314
+ if (!fs34.existsSync(csvPath)) continue;
3315
+ const stat = fs34.statSync(csvPath);
3316
3316
  if (stat.size < 50) continue;
3317
3317
  try {
3318
3318
  await dbManager.execute(
@@ -3325,8 +3325,8 @@ async function loadGraphToDB(graph, dbManager) {
3325
3325
  }
3326
3326
  let edgeCount = 0;
3327
3327
  for (const group of edgeGroups) {
3328
- if (!fs33.existsSync(group.filePath)) continue;
3329
- const stat = fs33.statSync(group.filePath);
3328
+ if (!fs34.existsSync(group.filePath)) continue;
3329
+ const stat = fs34.statSync(group.filePath);
3330
3330
  if (stat.size < 50) continue;
3331
3331
  try {
3332
3332
  await dbManager.execute(
@@ -3340,7 +3340,7 @@ async function loadGraphToDB(graph, dbManager) {
3340
3340
  return { nodeCount, edgeCount };
3341
3341
  } finally {
3342
3342
  try {
3343
- fs33.rmSync(tmpDir, { recursive: true, force: true });
3343
+ fs34.rmSync(tmpDir, { recursive: true, force: true });
3344
3344
  } catch {
3345
3345
  }
3346
3346
  }
@@ -3442,15 +3442,15 @@ var init_graph_loader = __esm({
3442
3442
  });
3443
3443
  function loadRegistry() {
3444
3444
  try {
3445
- const data = fs33.readFileSync(REPOS_FILE, "utf-8");
3445
+ const data = fs34.readFileSync(REPOS_FILE, "utf-8");
3446
3446
  return JSON.parse(data);
3447
3447
  } catch {
3448
3448
  return [];
3449
3449
  }
3450
3450
  }
3451
3451
  function saveRegistry(entries) {
3452
- fs33.mkdirSync(GLOBAL_DIR, { recursive: true });
3453
- fs33.writeFileSync(REPOS_FILE, JSON.stringify(entries, null, 2));
3452
+ fs34.mkdirSync(GLOBAL_DIR, { recursive: true });
3453
+ fs34.writeFileSync(REPOS_FILE, JSON.stringify(entries, null, 2));
3454
3454
  }
3455
3455
  function upsertRepo(entry) {
3456
3456
  const entries = loadRegistry();
@@ -3469,28 +3469,28 @@ function removeRepo(repoPath) {
3469
3469
  var GLOBAL_DIR, REPOS_FILE;
3470
3470
  var init_repo_registry = __esm({
3471
3471
  "src/storage/repo-registry.ts"() {
3472
- GLOBAL_DIR = path33.join(os12.homedir(), ".code-intel");
3473
- REPOS_FILE = path33.join(GLOBAL_DIR, "repos.json");
3472
+ GLOBAL_DIR = path35.join(os12.homedir(), ".code-intel");
3473
+ REPOS_FILE = path35.join(GLOBAL_DIR, "repos.json");
3474
3474
  }
3475
3475
  });
3476
3476
  function saveMetadata(repoDir, metadata) {
3477
- const metaDir = path33.join(repoDir, ".code-intel");
3478
- fs33.mkdirSync(metaDir, { recursive: true });
3479
- fs33.writeFileSync(path33.join(metaDir, "meta.json"), JSON.stringify(metadata, null, 2));
3477
+ const metaDir = path35.join(repoDir, ".code-intel");
3478
+ fs34.mkdirSync(metaDir, { recursive: true });
3479
+ fs34.writeFileSync(path35.join(metaDir, "meta.json"), JSON.stringify(metadata, null, 2));
3480
3480
  }
3481
3481
  function loadMetadata(repoDir) {
3482
3482
  try {
3483
- const data = fs33.readFileSync(path33.join(repoDir, ".code-intel", "meta.json"), "utf-8");
3483
+ const data = fs34.readFileSync(path35.join(repoDir, ".code-intel", "meta.json"), "utf-8");
3484
3484
  return JSON.parse(data);
3485
3485
  } catch {
3486
3486
  return null;
3487
3487
  }
3488
3488
  }
3489
3489
  function getDbPath(repoDir) {
3490
- return path33.join(repoDir, ".code-intel", "graph.db");
3490
+ return path35.join(repoDir, ".code-intel", "graph.db");
3491
3491
  }
3492
3492
  function getVectorDbPath(repoDir) {
3493
- return path33.join(repoDir, ".code-intel", "vector.db");
3493
+ return path35.join(repoDir, ".code-intel", "vector.db");
3494
3494
  }
3495
3495
  var init_metadata = __esm({
3496
3496
  "src/storage/metadata.ts"() {
@@ -3546,27 +3546,27 @@ __export(group_registry_exports, {
3546
3546
  saveSyncResult: () => saveSyncResult
3547
3547
  });
3548
3548
  function groupFile(name) {
3549
- return path33.join(GROUPS_DIR, `${name}.json`);
3549
+ return path35.join(GROUPS_DIR, `${name}.json`);
3550
3550
  }
3551
3551
  function loadGroup(name) {
3552
3552
  try {
3553
- return JSON.parse(fs33.readFileSync(groupFile(name), "utf-8"));
3553
+ return JSON.parse(fs34.readFileSync(groupFile(name), "utf-8"));
3554
3554
  } catch {
3555
3555
  return null;
3556
3556
  }
3557
3557
  }
3558
3558
  function saveGroup(group) {
3559
- fs33.mkdirSync(GROUPS_DIR, { recursive: true });
3560
- fs33.writeFileSync(groupFile(group.name), JSON.stringify(group, null, 2) + "\n");
3559
+ fs34.mkdirSync(GROUPS_DIR, { recursive: true });
3560
+ fs34.writeFileSync(groupFile(group.name), JSON.stringify(group, null, 2) + "\n");
3561
3561
  }
3562
3562
  function listGroups() {
3563
3563
  const groups = [];
3564
3564
  try {
3565
- for (const file of fs33.readdirSync(GROUPS_DIR)) {
3565
+ for (const file of fs34.readdirSync(GROUPS_DIR)) {
3566
3566
  if (!file.endsWith(".json") || file.endsWith(".sync.json")) continue;
3567
3567
  try {
3568
3568
  const g = JSON.parse(
3569
- fs33.readFileSync(path33.join(GROUPS_DIR, file), "utf-8")
3569
+ fs34.readFileSync(path35.join(GROUPS_DIR, file), "utf-8")
3570
3570
  );
3571
3571
  groups.push(g);
3572
3572
  } catch {
@@ -3578,16 +3578,16 @@ function listGroups() {
3578
3578
  }
3579
3579
  function deleteGroup(name) {
3580
3580
  try {
3581
- fs33.unlinkSync(groupFile(name));
3581
+ fs34.unlinkSync(groupFile(name));
3582
3582
  } catch {
3583
3583
  }
3584
3584
  try {
3585
- fs33.unlinkSync(path33.join(GROUPS_DIR, `${name}.sync.json`));
3585
+ fs34.unlinkSync(path35.join(GROUPS_DIR, `${name}.sync.json`));
3586
3586
  } catch {
3587
3587
  }
3588
3588
  }
3589
3589
  function groupExists(name) {
3590
- return fs33.existsSync(groupFile(name));
3590
+ return fs34.existsSync(groupFile(name));
3591
3591
  }
3592
3592
  function addMember(groupName, member) {
3593
3593
  const group = loadGroup(groupName);
@@ -3613,16 +3613,16 @@ function removeMember(groupName, groupPath) {
3613
3613
  return group;
3614
3614
  }
3615
3615
  function saveSyncResult(result) {
3616
- fs33.mkdirSync(GROUPS_DIR, { recursive: true });
3617
- fs33.writeFileSync(
3618
- path33.join(GROUPS_DIR, `${result.groupName}.sync.json`),
3616
+ fs34.mkdirSync(GROUPS_DIR, { recursive: true });
3617
+ fs34.writeFileSync(
3618
+ path35.join(GROUPS_DIR, `${result.groupName}.sync.json`),
3619
3619
  JSON.stringify(result, null, 2) + "\n"
3620
3620
  );
3621
3621
  }
3622
3622
  function loadSyncResult(groupName) {
3623
3623
  try {
3624
3624
  return JSON.parse(
3625
- fs33.readFileSync(path33.join(GROUPS_DIR, `${groupName}.sync.json`), "utf-8")
3625
+ fs34.readFileSync(path35.join(GROUPS_DIR, `${groupName}.sync.json`), "utf-8")
3626
3626
  );
3627
3627
  } catch {
3628
3628
  return null;
@@ -3631,7 +3631,7 @@ function loadSyncResult(groupName) {
3631
3631
  var GROUPS_DIR;
3632
3632
  var init_group_registry = __esm({
3633
3633
  "src/multi-repo/group-registry.ts"() {
3634
- GROUPS_DIR = path33.join(os12.homedir(), ".code-intel", "groups");
3634
+ GROUPS_DIR = path35.join(os12.homedir(), ".code-intel", "groups");
3635
3635
  }
3636
3636
  });
3637
3637
 
@@ -3716,12 +3716,12 @@ function scanForFiles(root, matcher, maxDepth = 2) {
3716
3716
  if (depth > maxDepth) return;
3717
3717
  let entries;
3718
3718
  try {
3719
- entries = fs33.readdirSync(dir, { withFileTypes: true });
3719
+ entries = fs34.readdirSync(dir, { withFileTypes: true });
3720
3720
  } catch {
3721
3721
  return;
3722
3722
  }
3723
3723
  for (const entry of entries) {
3724
- const full = path33.join(dir, entry.name);
3724
+ const full = path35.join(dir, entry.name);
3725
3725
  if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
3726
3726
  walk2(full, depth + 1);
3727
3727
  } else if (entry.isFile() && matcher(entry.name)) {
@@ -3737,8 +3737,8 @@ var init_file_scanner = __esm({
3737
3737
  }
3738
3738
  });
3739
3739
  function tryParseFile(filePath) {
3740
- const ext = path33.extname(filePath).toLowerCase();
3741
- const content = fs33.readFileSync(filePath, "utf-8");
3740
+ const ext = path35.extname(filePath).toLowerCase();
3741
+ const content = fs34.readFileSync(filePath, "utf-8");
3742
3742
  if (ext === ".json") {
3743
3743
  try {
3744
3744
  return JSON.parse(content);
@@ -3807,7 +3807,7 @@ async function parseGraphQLContracts(repoRoot) {
3807
3807
  const files = scanForFiles(repoRoot, (name) => name.endsWith(".graphql") || name.endsWith(".gql"));
3808
3808
  const contracts = [];
3809
3809
  for (const filePath of files) {
3810
- const content = fs33.readFileSync(filePath, "utf-8");
3810
+ const content = fs34.readFileSync(filePath, "utf-8");
3811
3811
  const typeRegex = /type\s+(\w+)\s*\{([^}]+)\}/g;
3812
3812
  let match;
3813
3813
  while ((match = typeRegex.exec(content)) !== null) {
@@ -3843,7 +3843,7 @@ async function parseProtoContracts(repoRoot) {
3843
3843
  const files = scanForFiles(repoRoot, (name) => name.endsWith(".proto"));
3844
3844
  const contracts = [];
3845
3845
  for (const filePath of files) {
3846
- const content = fs33.readFileSync(filePath, "utf-8");
3846
+ const content = fs34.readFileSync(filePath, "utf-8");
3847
3847
  const serviceRegex = /service\s+(\w+)\s*\{([^}]+)\}/g;
3848
3848
  let serviceMatch;
3849
3849
  while ((serviceMatch = serviceRegex.exec(content)) !== null) {
@@ -4061,8 +4061,8 @@ async function syncGroup(group) {
4061
4061
  logger_default.warn(` \u26A0 Registry entry "${member.registryName}" not found \u2014 skipping ${member.groupPath}`);
4062
4062
  continue;
4063
4063
  }
4064
- const dbPath = path33.join(regEntry.path, ".code-intel", "graph.db");
4065
- if (!fs33.existsSync(dbPath)) {
4064
+ const dbPath = path35.join(regEntry.path, ".code-intel", "graph.db");
4065
+ if (!fs34.existsSync(dbPath)) {
4066
4066
  logger_default.warn(` \u26A0 No index at ${dbPath} \u2014 run \`code-intel analyze ${regEntry.path}\` first`);
4067
4067
  continue;
4068
4068
  }
@@ -4175,10 +4175,10 @@ var init_codes = __esm({
4175
4175
  }
4176
4176
  });
4177
4177
  function secureMkdir(dir) {
4178
- fs33.mkdirSync(dir, { recursive: true, mode: SECURE_DIR_MODE });
4178
+ fs34.mkdirSync(dir, { recursive: true, mode: SECURE_DIR_MODE });
4179
4179
  if (process.platform !== "win32") {
4180
4180
  try {
4181
- fs33.chmodSync(dir, SECURE_DIR_MODE);
4181
+ fs34.chmodSync(dir, SECURE_DIR_MODE);
4182
4182
  } catch {
4183
4183
  }
4184
4184
  }
@@ -4186,22 +4186,22 @@ function secureMkdir(dir) {
4186
4186
  function secureChmodFile(file) {
4187
4187
  if (process.platform === "win32") return;
4188
4188
  try {
4189
- fs33.chmodSync(file, SECURE_FILE_MODE);
4189
+ fs34.chmodSync(file, SECURE_FILE_MODE);
4190
4190
  } catch {
4191
4191
  }
4192
4192
  }
4193
4193
  function secureWriteFile(file, data) {
4194
- secureMkdir(path33.dirname(file));
4195
- fs33.writeFileSync(file, data, { mode: SECURE_FILE_MODE });
4194
+ secureMkdir(path35.dirname(file));
4195
+ fs34.writeFileSync(file, data, { mode: SECURE_FILE_MODE });
4196
4196
  secureChmodFile(file);
4197
4197
  }
4198
4198
  function tightenDbFiles(dir) {
4199
4199
  if (process.platform === "win32") return;
4200
- if (!fs33.existsSync(dir)) return;
4201
- for (const name of fs33.readdirSync(dir)) {
4200
+ if (!fs34.existsSync(dir)) return;
4201
+ for (const name of fs34.readdirSync(dir)) {
4202
4202
  if (name.endsWith(".db") || name.endsWith(".db-wal") || name.endsWith(".db-shm")) {
4203
4203
  try {
4204
- fs33.chmodSync(path33.join(dir, name), SECURE_FILE_MODE);
4204
+ fs34.chmodSync(path35.join(dir, name), SECURE_FILE_MODE);
4205
4205
  } catch {
4206
4206
  }
4207
4207
  }
@@ -4215,7 +4215,7 @@ var init_fs_secure = __esm({
4215
4215
  }
4216
4216
  });
4217
4217
  function getUsersDBPath() {
4218
- return process.env["CODE_INTEL_USERS_DB_PATH"] ?? path33.join(os12.homedir(), ".code-intel", "users.db");
4218
+ return process.env["CODE_INTEL_USERS_DB_PATH"] ?? path35.join(os12.homedir(), ".code-intel", "users.db");
4219
4219
  }
4220
4220
  function getOrCreateUsersDB() {
4221
4221
  if (!_usersDB) {
@@ -4231,7 +4231,7 @@ var init_users_db = __esm({
4231
4231
  UsersDB = class {
4232
4232
  db;
4233
4233
  constructor(dbPath) {
4234
- const dir = path33.dirname(dbPath);
4234
+ const dir = path35.dirname(dbPath);
4235
4235
  secureMkdir(dir);
4236
4236
  this.db = new Database(dbPath);
4237
4237
  this.db.pragma("journal_mode = WAL");
@@ -4508,7 +4508,7 @@ function getScryptN() {
4508
4508
  return Number.isInteger(v) && v >= 1024 ? v : 1 << 14;
4509
4509
  }
4510
4510
  function getSecretsPath() {
4511
- return process.env["CODE_INTEL_SECRETS_PATH"] ?? path33.join(os12.homedir(), ".code-intel", ".secrets");
4511
+ return process.env["CODE_INTEL_SECRETS_PATH"] ?? path35.join(os12.homedir(), ".code-intel", ".secrets");
4512
4512
  }
4513
4513
  function getMasterPassword() {
4514
4514
  const fromEnv = process.env["CODE_INTEL_SECRET_KEY"];
@@ -4550,12 +4550,12 @@ function decryptSecrets(encrypted) {
4550
4550
  return JSON.parse(plaintext.toString("utf8"));
4551
4551
  }
4552
4552
  function loadSecrets(secretsPath = getSecretsPath()) {
4553
- if (!fs33.existsSync(secretsPath)) return {};
4554
- const blob = fs33.readFileSync(secretsPath);
4553
+ if (!fs34.existsSync(secretsPath)) return {};
4554
+ const blob = fs34.readFileSync(secretsPath);
4555
4555
  return decryptSecrets(blob);
4556
4556
  }
4557
4557
  function saveSecrets(blob, secretsPath = getSecretsPath()) {
4558
- secureMkdir(path33.dirname(secretsPath));
4558
+ secureMkdir(path35.dirname(secretsPath));
4559
4559
  const encrypted = encryptSecrets(blob);
4560
4560
  secureWriteFile(secretsPath, encrypted);
4561
4561
  secureChmodFile(secretsPath);
@@ -6039,14 +6039,14 @@ function detectDeadCode(graph) {
6039
6039
  if (meta?.deprecated === true) continue;
6040
6040
  if (ENTRY_POINT_NAME_RE.test(node.name)) continue;
6041
6041
  if (entryPointIds.has(node.id)) continue;
6042
- let hasCallers = false;
6042
+ let hasCallers2 = false;
6043
6043
  for (const edge of graph.findEdgesTo(node.id)) {
6044
6044
  if (edge.kind === "calls") {
6045
- hasCallers = true;
6045
+ hasCallers2 = true;
6046
6046
  break;
6047
6047
  }
6048
6048
  }
6049
- if (hasCallers) continue;
6049
+ if (hasCallers2) continue;
6050
6050
  let hasImporters = false;
6051
6051
  for (const edge of graph.findEdgesTo(node.id)) {
6052
6052
  if (edge.kind === "imports") {
@@ -6298,6 +6298,550 @@ var init_health_score = __esm({
6298
6298
  }
6299
6299
  });
6300
6300
 
6301
+ // src/analysis/deprecated-detector.ts
6302
+ var deprecated_detector_exports = {};
6303
+ __export(deprecated_detector_exports, {
6304
+ DeprecatedDetector: () => DeprecatedDetector
6305
+ });
6306
+ var BUILTIN_DEPRECATED, DeprecatedDetector;
6307
+ var init_deprecated_detector = __esm({
6308
+ "src/analysis/deprecated-detector.ts"() {
6309
+ BUILTIN_DEPRECATED = [
6310
+ { pattern: "url.parse", message: "deprecated in Node.js v11.0.0 \u2014 use the WHATWG URL API instead" },
6311
+ { pattern: "url.resolve", message: "deprecated in Node.js v11.0.0 \u2014 use the WHATWG URL API instead" },
6312
+ { pattern: "url.format", message: "deprecated in Node.js v11.0.0 \u2014 use the WHATWG URL API instead" },
6313
+ { pattern: "fs.exists", message: "deprecated \u2014 use fs.access instead" },
6314
+ { pattern: "crypto.createCipher", message: "deprecated \u2014 use crypto.createCipheriv instead" },
6315
+ { pattern: "crypto.createDecipher", message: "deprecated \u2014 use crypto.createDecipheriv instead" },
6316
+ { pattern: "new Buffer()", message: "deprecated \u2014 use Buffer.from() instead" },
6317
+ { pattern: "domain.create", message: "deprecated \u2014 the domain module is discouraged" },
6318
+ { pattern: "process.binding", message: "deprecated internal API" }
6319
+ ];
6320
+ DeprecatedDetector = class {
6321
+ tagDeprecated(graph) {
6322
+ for (const node of graph.allNodes()) {
6323
+ if (!node.metadata) node.metadata = {};
6324
+ if (node.metadata["deprecated"] === true) continue;
6325
+ let message;
6326
+ const jsdoc = node.metadata["jsdoc"];
6327
+ const comment = node.metadata["comment"];
6328
+ if (jsdoc?.includes("@deprecated") || comment?.includes("@deprecated")) {
6329
+ const src = jsdoc ?? comment ?? "";
6330
+ const match = src.match(/@deprecated\s+(.*)/);
6331
+ message = match?.[1]?.trim() || "deprecated";
6332
+ }
6333
+ if (!message && node.metadata["deprecated"] === true) {
6334
+ message = node.metadata["deprecationMessage"] ?? "deprecated";
6335
+ }
6336
+ if (!message) {
6337
+ const annotations = node.metadata["annotations"];
6338
+ if (Array.isArray(annotations) && annotations.includes("Deprecated")) {
6339
+ message = "marked @Deprecated";
6340
+ }
6341
+ }
6342
+ if (!message) {
6343
+ const attributes = node.metadata["attributes"];
6344
+ if (Array.isArray(attributes) && attributes.includes("deprecated")) {
6345
+ message = "marked #[deprecated]";
6346
+ }
6347
+ }
6348
+ if (!message) {
6349
+ for (const entry of BUILTIN_DEPRECATED) {
6350
+ if (node.name === entry.pattern || node.name.includes(entry.pattern)) {
6351
+ message = entry.message;
6352
+ break;
6353
+ }
6354
+ }
6355
+ }
6356
+ if (message) {
6357
+ node.metadata["deprecated"] = true;
6358
+ node.metadata["deprecationMessage"] = message;
6359
+ }
6360
+ }
6361
+ }
6362
+ detect(graph, scope) {
6363
+ const findings = [];
6364
+ for (const node of graph.allNodes()) {
6365
+ if (!node.metadata?.["deprecated"]) continue;
6366
+ const callers = [];
6367
+ for (const edge of graph.findEdgesTo(node.id)) {
6368
+ if (edge.kind !== "calls" && edge.kind !== "deprecated_use") continue;
6369
+ const caller = graph.getNode(edge.source);
6370
+ if (!caller) continue;
6371
+ if (scope && !caller.filePath.includes(scope)) continue;
6372
+ callers.push({ name: caller.name, filePath: caller.filePath });
6373
+ const edgeId = `dep_use_${edge.source}_${node.id}`;
6374
+ if (!graph.getEdge(edgeId)) {
6375
+ graph.addEdge({ id: edgeId, source: edge.source, target: node.id, kind: "deprecated_use" });
6376
+ }
6377
+ }
6378
+ findings.push({
6379
+ symbol: node.name,
6380
+ filePath: node.filePath,
6381
+ deprecationMessage: node.metadata?.["deprecationMessage"] ?? "deprecated",
6382
+ callers
6383
+ });
6384
+ }
6385
+ return findings;
6386
+ }
6387
+ };
6388
+ }
6389
+ });
6390
+
6391
+ // src/analysis/complexity.ts
6392
+ var complexity_exports = {};
6393
+ __export(complexity_exports, {
6394
+ computeComplexity: () => computeComplexity
6395
+ });
6396
+ function getSeverity(cyclomatic) {
6397
+ if (cyclomatic <= 5) return "LOW";
6398
+ if (cyclomatic <= 10) return "MEDIUM";
6399
+ if (cyclomatic <= 20) return "HIGH";
6400
+ return "CRITICAL";
6401
+ }
6402
+ function computeComplexity(graph, scope) {
6403
+ const results = [];
6404
+ for (const node of graph.allNodes()) {
6405
+ if (node.kind !== "function" && node.kind !== "method") continue;
6406
+ if (scope && !node.filePath.startsWith(scope)) continue;
6407
+ let outgoingCalls = 0;
6408
+ for (const edge of graph.findEdgesFrom(node.id)) {
6409
+ if (edge.kind === "calls") outgoingCalls++;
6410
+ }
6411
+ let cyclomatic;
6412
+ const meta = node.metadata;
6413
+ const metaComplexity = meta?.complexity;
6414
+ if (typeof metaComplexity?.cyclomatic === "number") {
6415
+ cyclomatic = metaComplexity.cyclomatic;
6416
+ } else {
6417
+ cyclomatic = 1 + Math.floor(outgoingCalls / 2);
6418
+ }
6419
+ cyclomatic = Math.min(cyclomatic, 50);
6420
+ let cognitive;
6421
+ if (typeof metaComplexity?.cognitive === "number") {
6422
+ cognitive = metaComplexity.cognitive;
6423
+ } else {
6424
+ cognitive = Math.ceil(cyclomatic * 1.3);
6425
+ }
6426
+ results.push({
6427
+ nodeId: node.id,
6428
+ name: node.name,
6429
+ filePath: node.filePath,
6430
+ cyclomatic,
6431
+ cognitive,
6432
+ severity: getSeverity(cyclomatic)
6433
+ });
6434
+ }
6435
+ return results.sort((a, b) => b.cyclomatic - a.cyclomatic);
6436
+ }
6437
+ var init_complexity = __esm({
6438
+ "src/analysis/complexity.ts"() {
6439
+ }
6440
+ });
6441
+
6442
+ // src/analysis/test-coverage.ts
6443
+ var test_coverage_exports = {};
6444
+ __export(test_coverage_exports, {
6445
+ computeCoverage: () => computeCoverage
6446
+ });
6447
+ function isTestFile(filePath) {
6448
+ if (filePath.includes(".test.") || filePath.includes(".spec.")) return true;
6449
+ if (filePath.includes("_test.") || filePath.endsWith("_test.go")) return true;
6450
+ if (filePath.includes("__tests__")) return true;
6451
+ const base = path35.basename(filePath);
6452
+ if (base.startsWith("Test") && filePath.endsWith(".java")) return true;
6453
+ return false;
6454
+ }
6455
+ function computeBlastRadius(graph, nodeId) {
6456
+ const visited = /* @__PURE__ */ new Set();
6457
+ const queue = [{ id: nodeId, depth: 0 }];
6458
+ while (queue.length > 0) {
6459
+ const { id, depth } = queue.shift();
6460
+ if (visited.has(id)) continue;
6461
+ visited.add(id);
6462
+ if (depth >= 3) continue;
6463
+ for (const edge of graph.findEdgesTo(id)) {
6464
+ if (edge.kind === "calls" || edge.kind === "imports") {
6465
+ if (!visited.has(edge.source)) {
6466
+ queue.push({ id: edge.source, depth: depth + 1 });
6467
+ }
6468
+ }
6469
+ }
6470
+ }
6471
+ return Math.max(0, visited.size - 1);
6472
+ }
6473
+ function getRisk(blastRadius) {
6474
+ if (blastRadius > 20) return "HIGH";
6475
+ if (blastRadius >= 5) return "MEDIUM";
6476
+ return "LOW";
6477
+ }
6478
+ function computeCoverage(graph, scope) {
6479
+ const testFilePaths = /* @__PURE__ */ new Set();
6480
+ for (const node of graph.allNodes()) {
6481
+ if (isTestFile(node.filePath)) testFilePaths.add(node.filePath);
6482
+ }
6483
+ const nodesImportedByTests = /* @__PURE__ */ new Set();
6484
+ for (const edge of graph.findEdgesByKind("imports")) {
6485
+ const sourceNode = graph.getNode(edge.source);
6486
+ if (sourceNode && isTestFile(sourceNode.filePath)) {
6487
+ nodesImportedByTests.add(edge.target);
6488
+ }
6489
+ }
6490
+ const nodesWithTestedBy = /* @__PURE__ */ new Set();
6491
+ for (const edge of graph.findEdgesByKind("tested_by")) {
6492
+ nodesWithTestedBy.add(edge.source);
6493
+ }
6494
+ const baseNameToTestFiles = /* @__PURE__ */ new Map();
6495
+ for (const testPath of testFilePaths) {
6496
+ const base = path35.basename(testPath);
6497
+ const stripped = base.replace(/\.test\.[^.]+$/, "").replace(/\.spec\.[^.]+$/, "").replace(/_test\.[^.]+$/, "").replace(/_test$/, "");
6498
+ const existing = baseNameToTestFiles.get(stripped) ?? [];
6499
+ existing.push(testPath);
6500
+ baseNameToTestFiles.set(stripped, existing);
6501
+ }
6502
+ const exportedKinds = /* @__PURE__ */ new Set(["function", "method", "class"]);
6503
+ const results = [];
6504
+ for (const node of graph.allNodes()) {
6505
+ if (!exportedKinds.has(node.kind)) continue;
6506
+ if (node.exported !== true) continue;
6507
+ if (scope && !node.filePath.startsWith(scope)) continue;
6508
+ const testFiles = [];
6509
+ if (nodesWithTestedBy.has(node.id)) {
6510
+ for (const edge of graph.findEdgesFrom(node.id)) {
6511
+ if (edge.kind === "tested_by") {
6512
+ const testNode = graph.getNode(edge.target);
6513
+ if (testNode && !testFiles.includes(testNode.filePath)) {
6514
+ testFiles.push(testNode.filePath);
6515
+ }
6516
+ }
6517
+ }
6518
+ }
6519
+ if (nodesImportedByTests.has(node.id)) {
6520
+ for (const edge of graph.findEdgesTo(node.id)) {
6521
+ if (edge.kind === "imports") {
6522
+ const sourceNode = graph.getNode(edge.source);
6523
+ if (sourceNode && isTestFile(sourceNode.filePath) && !testFiles.includes(sourceNode.filePath)) {
6524
+ testFiles.push(sourceNode.filePath);
6525
+ }
6526
+ }
6527
+ }
6528
+ }
6529
+ const nodeBase = path35.basename(node.filePath).replace(/\.[^.]+$/, "");
6530
+ const matchingTestFiles = baseNameToTestFiles.get(nodeBase) ?? [];
6531
+ for (const tf of matchingTestFiles) {
6532
+ if (!testFiles.includes(tf)) testFiles.push(tf);
6533
+ }
6534
+ const tested = testFiles.length > 0;
6535
+ const blastRadius = computeBlastRadius(graph, node.id);
6536
+ const risk = getRisk(blastRadius);
6537
+ results.push({
6538
+ nodeId: node.id,
6539
+ name: node.name,
6540
+ filePath: node.filePath,
6541
+ exported: true,
6542
+ tested,
6543
+ testFiles,
6544
+ blastRadius,
6545
+ risk
6546
+ });
6547
+ }
6548
+ const totalExported = results.length;
6549
+ const testedExported = results.filter((r) => r.tested).length;
6550
+ const coveragePct = totalExported === 0 ? 100 : Math.round(testedExported / totalExported * 100);
6551
+ const untestedByRisk = results.filter((r) => !r.tested).sort((a, b) => b.blastRadius - a.blastRadius);
6552
+ return { totalExported, testedExported, coveragePct, untestedByRisk };
6553
+ }
6554
+ var init_test_coverage = __esm({
6555
+ "src/analysis/test-coverage.ts"() {
6556
+ }
6557
+ });
6558
+
6559
+ // src/security/secret-scanner.ts
6560
+ var secret_scanner_exports = {};
6561
+ __export(secret_scanner_exports, {
6562
+ SecretScanner: () => SecretScanner
6563
+ });
6564
+ function shannonEntropy(s) {
6565
+ const freq = /* @__PURE__ */ new Map();
6566
+ for (const c of s) freq.set(c, (freq.get(c) ?? 0) + 1);
6567
+ let entropy = 0;
6568
+ for (const count of freq.values()) {
6569
+ const p = count / s.length;
6570
+ entropy -= p * Math.log2(p);
6571
+ }
6572
+ return entropy;
6573
+ }
6574
+ function isTestFile2(filePath) {
6575
+ return filePath.includes(".test.") || filePath.includes(".spec.") || filePath.includes("fixtures/") || filePath.includes("mocks/");
6576
+ }
6577
+ var ENV_VAR_RE, SENSITIVE_NAME_RE, VALUE_PATTERNS, SecretScanner;
6578
+ var init_secret_scanner = __esm({
6579
+ "src/security/secret-scanner.ts"() {
6580
+ ENV_VAR_RE = /^process\.env\./;
6581
+ SENSITIVE_NAME_RE = /_SECRET$|_PASSWORD$|_TOKEN$|_KEY$|_API_KEY$/i;
6582
+ VALUE_PATTERNS = [
6583
+ [/sk-[A-Za-z0-9]{6,}/, "openai-api-key", "HIGH"],
6584
+ [/pk_live_[A-Za-z0-9]{20,}/, "stripe-key", "HIGH"],
6585
+ [/AKIA[0-9A-Z]{16}|aws.access.key/i, "aws-access-key", "HIGH"],
6586
+ [/xoxb-[0-9]{11}-[0-9]{11}-[A-Za-z0-9]{24}/, "slack-token", "HIGH"],
6587
+ [/postgres:\/\/[^@]+:[^@]+@/, "db-url-with-credentials", "HIGH"],
6588
+ [/mysql:\/\/[^@]+:[^@]+@/, "db-url-with-credentials", "HIGH"],
6589
+ [/-----BEGIN RSA PRIVATE KEY-----/, "rsa-private-key", "HIGH"]
6590
+ ];
6591
+ SecretScanner = class {
6592
+ scan(graph, options) {
6593
+ const findings = [];
6594
+ const includeTests = options?.includeTestFiles ?? false;
6595
+ const scope = options?.scope;
6596
+ const ignorePatterns = [...options?.ignorePatterns ?? []];
6597
+ if (options?.workspaceRoot) {
6598
+ try {
6599
+ const raw = fs34.readFileSync(path35.join(options.workspaceRoot, ".codeintelignore"), "utf-8");
6600
+ for (const line of raw.split("\n")) {
6601
+ const trimmed = line.trim();
6602
+ if (trimmed && !trimmed.startsWith("#")) ignorePatterns.push(trimmed);
6603
+ }
6604
+ } catch {
6605
+ }
6606
+ }
6607
+ for (const node of graph.allNodes()) {
6608
+ const filePath = node.filePath;
6609
+ if (scope && !filePath.startsWith(scope)) continue;
6610
+ if (!includeTests && isTestFile2(filePath)) continue;
6611
+ if (ignorePatterns.length > 0 && ignorePatterns.some((p) => filePath.includes(p))) continue;
6612
+ const meta = node.metadata;
6613
+ const rawValue = meta?.value ?? meta?.literalValue;
6614
+ if (typeof rawValue !== "string" || rawValue.trim() === "") continue;
6615
+ const value = rawValue.trim();
6616
+ if (ENV_VAR_RE.test(value)) continue;
6617
+ let matched = false;
6618
+ for (const [re, label, severity] of VALUE_PATTERNS) {
6619
+ if (re.test(value)) {
6620
+ node.metadata = {
6621
+ ...node.metadata ?? {},
6622
+ security: { secretRisk: true, secretPattern: label }
6623
+ };
6624
+ findings.push({
6625
+ file: filePath,
6626
+ line: node.startLine,
6627
+ symbol: node.name,
6628
+ pattern: label,
6629
+ severity
6630
+ });
6631
+ matched = true;
6632
+ break;
6633
+ }
6634
+ }
6635
+ if (matched) continue;
6636
+ if (SENSITIVE_NAME_RE.test(node.name)) {
6637
+ node.metadata = {
6638
+ ...node.metadata ?? {},
6639
+ security: { secretRisk: true, secretPattern: "sensitive-name-with-value" }
6640
+ };
6641
+ findings.push({
6642
+ file: filePath,
6643
+ line: node.startLine,
6644
+ symbol: node.name,
6645
+ pattern: "sensitive-name-with-value",
6646
+ severity: "MEDIUM"
6647
+ });
6648
+ continue;
6649
+ }
6650
+ if (SENSITIVE_NAME_RE.test(node.name) && value.length > 20 && shannonEntropy(value) > 4.5) {
6651
+ node.metadata = {
6652
+ ...node.metadata ?? {},
6653
+ security: { secretRisk: true, secretPattern: "high-entropy-string" }
6654
+ };
6655
+ findings.push({
6656
+ file: filePath,
6657
+ line: node.startLine,
6658
+ symbol: node.name,
6659
+ pattern: "high-entropy-string",
6660
+ severity: "MEDIUM"
6661
+ });
6662
+ }
6663
+ }
6664
+ return findings;
6665
+ }
6666
+ };
6667
+ }
6668
+ });
6669
+
6670
+ // src/security/vulnerability-detector.ts
6671
+ var vulnerability_detector_exports = {};
6672
+ __export(vulnerability_detector_exports, {
6673
+ VulnerabilityDetector: () => VulnerabilityDetector
6674
+ });
6675
+ function hasCallers(graph, nodeId) {
6676
+ for (const edge of graph.findEdgesTo(nodeId)) {
6677
+ if (edge.kind === "calls") return true;
6678
+ }
6679
+ return false;
6680
+ }
6681
+ var CWE, SQL_PATTERN, XSS_PATTERN, SSRF_PATTERN, PATH_PATTERN, CMD_PATTERN, VulnerabilityDetector;
6682
+ var init_vulnerability_detector = __esm({
6683
+ "src/security/vulnerability-detector.ts"() {
6684
+ CWE = {
6685
+ SQL_INJECTION: "CWE-89",
6686
+ XSS: "CWE-79",
6687
+ SSRF: "CWE-918",
6688
+ PATH_TRAVERSAL: "CWE-22",
6689
+ COMMAND_INJECTION: "CWE-78"
6690
+ };
6691
+ SQL_PATTERN = /(db|database|connection|knex|sequelize|pool)\.(query|execute|raw)/i;
6692
+ XSS_PATTERN = /innerHTML|outerHTML|document\.write|insertAdjacentHTML/i;
6693
+ SSRF_PATTERN = /^(fetch|axios|http\.request|got)$/i;
6694
+ PATH_PATTERN = /^(fs\.readFile|fs\.writeFile|path\.join|createReadStream)$/i;
6695
+ CMD_PATTERN = /^(exec|execSync|spawn|eval)$/i;
6696
+ VulnerabilityDetector = class {
6697
+ detect(graph, options) {
6698
+ const findings = [];
6699
+ const scope = options?.scope;
6700
+ const types = options?.types ? new Set(options.types) : null;
6701
+ const want = (t) => !types || types.has(t);
6702
+ const nodes = [...graph.allNodes()];
6703
+ for (const node of nodes) {
6704
+ if (node.kind === "vulnerability") continue;
6705
+ const filePath = node.filePath;
6706
+ if (scope && !filePath.startsWith(scope)) continue;
6707
+ const nodeName = node.name;
6708
+ const meta = node.metadata;
6709
+ if (want("SQL_INJECTION") && SQL_PATTERN.test(nodeName)) {
6710
+ const hasDynamic = meta?.hasStringConcatenation === true || hasCallers(graph, node.id);
6711
+ if (hasDynamic) {
6712
+ findings.push({
6713
+ type: "SQL_INJECTION",
6714
+ severity: "MEDIUM",
6715
+ file: filePath,
6716
+ line: node.startLine,
6717
+ symbol: nodeName,
6718
+ description: `Potential SQL injection: ${nodeName} may execute unsanitized user input`,
6719
+ cweId: CWE["SQL_INJECTION"]
6720
+ });
6721
+ this._tagNode(graph, node.id, "SQL_INJECTION");
6722
+ }
6723
+ }
6724
+ if (want("XSS") && XSS_PATTERN.test(nodeName)) {
6725
+ findings.push({
6726
+ type: "XSS",
6727
+ severity: "HIGH",
6728
+ file: filePath,
6729
+ line: node.startLine,
6730
+ symbol: nodeName,
6731
+ description: `Potential XSS: ${nodeName} writes to DOM sink`,
6732
+ cweId: CWE["XSS"]
6733
+ });
6734
+ this._tagNode(graph, node.id, "XSS");
6735
+ }
6736
+ if (want("XSS")) {
6737
+ for (const edge of graph.findEdgesFrom(node.id)) {
6738
+ if (edge.kind === "calls") {
6739
+ const callee = graph.getNode(edge.target);
6740
+ if (callee && XSS_PATTERN.test(callee.name)) {
6741
+ findings.push({
6742
+ type: "XSS",
6743
+ severity: "HIGH",
6744
+ file: filePath,
6745
+ line: node.startLine,
6746
+ symbol: node.name,
6747
+ description: `Potential XSS: ${node.name} calls ${callee.name}`,
6748
+ cweId: CWE["XSS"]
6749
+ });
6750
+ this._tagNode(graph, node.id, "XSS");
6751
+ }
6752
+ }
6753
+ }
6754
+ }
6755
+ if (want("SSRF") && SSRF_PATTERN.test(nodeName)) {
6756
+ const isDynamic = meta?.dynamicUrl === true;
6757
+ if (isDynamic) {
6758
+ findings.push({
6759
+ type: "SSRF",
6760
+ severity: "HIGH",
6761
+ file: filePath,
6762
+ line: node.startLine,
6763
+ symbol: nodeName,
6764
+ description: `Potential SSRF: ${nodeName} uses a dynamic URL`,
6765
+ cweId: CWE["SSRF"]
6766
+ });
6767
+ this._tagNode(graph, node.id, "SSRF");
6768
+ }
6769
+ }
6770
+ if (want("PATH_TRAVERSAL") && PATH_PATTERN.test(nodeName)) {
6771
+ const isDynamic = meta?.dynamicPath === true;
6772
+ if (isDynamic) {
6773
+ findings.push({
6774
+ type: "PATH_TRAVERSAL",
6775
+ severity: "HIGH",
6776
+ file: filePath,
6777
+ line: node.startLine,
6778
+ symbol: nodeName,
6779
+ description: `Potential path traversal: ${nodeName} uses a dynamic path`,
6780
+ cweId: CWE["PATH_TRAVERSAL"]
6781
+ });
6782
+ this._tagNode(graph, node.id, "PATH_TRAVERSAL");
6783
+ }
6784
+ }
6785
+ if (want("COMMAND_INJECTION") && CMD_PATTERN.test(nodeName)) {
6786
+ const isDynamic = meta?.dynamicArgs === true;
6787
+ if (isDynamic) {
6788
+ findings.push({
6789
+ type: "COMMAND_INJECTION",
6790
+ severity: "HIGH",
6791
+ file: filePath,
6792
+ line: node.startLine,
6793
+ symbol: nodeName,
6794
+ description: `Potential command injection: ${nodeName} uses dynamic arguments`,
6795
+ cweId: CWE["COMMAND_INJECTION"]
6796
+ });
6797
+ this._tagNode(graph, node.id, "COMMAND_INJECTION");
6798
+ }
6799
+ }
6800
+ }
6801
+ const seen = /* @__PURE__ */ new Set();
6802
+ return findings.filter((f) => {
6803
+ const key = `${f.type}:${f.file}:${f.line}:${f.symbol}`;
6804
+ if (seen.has(key)) return false;
6805
+ seen.add(key);
6806
+ return true;
6807
+ });
6808
+ }
6809
+ _tagNode(graph, nodeId, vulnType) {
6810
+ const node = graph.getNode(nodeId);
6811
+ if (!node) return;
6812
+ const meta = node.metadata ?? {};
6813
+ node.metadata = {
6814
+ ...meta,
6815
+ security: {
6816
+ ...meta["security"] ?? {},
6817
+ vulnerability: vulnType
6818
+ }
6819
+ };
6820
+ const vulnNodeId = `vuln:${vulnType}:${nodeId}`;
6821
+ if (!graph.getNode(vulnNodeId)) {
6822
+ graph.addNode({
6823
+ id: vulnNodeId,
6824
+ kind: "vulnerability",
6825
+ name: `${vulnType}:${node.name}`,
6826
+ filePath: node.filePath,
6827
+ startLine: node.startLine,
6828
+ metadata: { cweId: CWE[vulnType], vulnType }
6829
+ });
6830
+ }
6831
+ const edgeId = `has_vulnerability:${nodeId}:${vulnNodeId}`;
6832
+ if (!graph.getEdge(edgeId)) {
6833
+ graph.addEdge({
6834
+ id: edgeId,
6835
+ source: nodeId,
6836
+ target: vulnNodeId,
6837
+ kind: "has_vulnerability"
6838
+ });
6839
+ }
6840
+ }
6841
+ };
6842
+ }
6843
+ });
6844
+
6301
6845
  // src/pipeline/file-watcher.ts
6302
6846
  var file_watcher_exports = {};
6303
6847
  __export(file_watcher_exports, {
@@ -6376,10 +6920,10 @@ var init_file_watcher = __esm({
6376
6920
  }
6377
6921
  // ── private ─────────────────────────────────────────────────────────────────
6378
6922
  readCodeIntelIgnore() {
6379
- const ignoreFile = path33.join(this.workspaceRoot, ".codeintelignore");
6923
+ const ignoreFile = path35.join(this.workspaceRoot, ".codeintelignore");
6380
6924
  try {
6381
- if (!fs33.existsSync(ignoreFile)) return [];
6382
- return fs33.readFileSync(ignoreFile, "utf-8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
6925
+ if (!fs34.existsSync(ignoreFile)) return [];
6926
+ return fs34.readFileSync(ignoreFile, "utf-8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
6383
6927
  } catch {
6384
6928
  return [];
6385
6929
  }
@@ -6431,7 +6975,7 @@ var init_incremental_indexer = __esm({
6431
6975
  }
6432
6976
  const nodeIdsToRemove = /* @__PURE__ */ new Set();
6433
6977
  for (const absPath of changedFiles) {
6434
- const relPath2 = path33.relative(workspaceRoot, absPath);
6978
+ const relPath2 = path35.relative(workspaceRoot, absPath);
6435
6979
  for (const id of nodesByFilePath.get(relPath2) ?? []) nodeIdsToRemove.add(id);
6436
6980
  for (const id of nodesByFilePath.get(absPath) ?? []) nodeIdsToRemove.add(id);
6437
6981
  }
@@ -6439,12 +6983,12 @@ var init_incremental_indexer = __esm({
6439
6983
  graph.removeNodeCascade(id);
6440
6984
  nodesRemoved++;
6441
6985
  }
6442
- if (fs33.existsSync(dbPath)) {
6986
+ if (fs34.existsSync(dbPath)) {
6443
6987
  try {
6444
6988
  const db = new DbManager(dbPath);
6445
6989
  await db.init();
6446
6990
  for (const absPath of changedFiles) {
6447
- const relPath2 = path33.relative(workspaceRoot, absPath);
6991
+ const relPath2 = path35.relative(workspaceRoot, absPath);
6448
6992
  await removeNodesForFile(relPath2, db);
6449
6993
  }
6450
6994
  db.close();
@@ -6454,7 +6998,7 @@ var init_incremental_indexer = __esm({
6454
6998
  }
6455
6999
  const existingFiles = changedFiles.filter((f) => {
6456
7000
  try {
6457
- return fs33.statSync(f).isFile();
7001
+ return fs34.statSync(f).isFile();
6458
7002
  } catch {
6459
7003
  return false;
6460
7004
  }
@@ -6476,13 +7020,13 @@ var init_incremental_indexer = __esm({
6476
7020
  await runPipeline([noopScan, parsePhase, resolvePhase], context2);
6477
7021
  }
6478
7022
  const nodesAdded = Math.max(0, graph.size.nodes - (nodesBeforeParse - nodesRemoved));
6479
- if (fs33.existsSync(dbPath) && existingFiles.length > 0) {
7023
+ if (fs34.existsSync(dbPath) && existingFiles.length > 0) {
6480
7024
  try {
6481
7025
  const db = new DbManager(dbPath);
6482
7026
  await db.init();
6483
- const changedRelPaths = new Set(changedFiles.map((f) => path33.relative(workspaceRoot, f)));
7027
+ const changedRelPaths = new Set(changedFiles.map((f) => path35.relative(workspaceRoot, f)));
6484
7028
  const nodesToUpsert = [...graph.allNodes()].filter(
6485
- (n) => changedRelPaths.has(n.filePath) || changedRelPaths.has(path33.relative(workspaceRoot, n.filePath))
7029
+ (n) => changedRelPaths.has(n.filePath) || changedRelPaths.has(path35.relative(workspaceRoot, n.filePath))
6486
7030
  );
6487
7031
  await upsertNodes(nodesToUpsert, db);
6488
7032
  db.close();
@@ -6513,35 +7057,35 @@ __export(saved_queries_exports, {
6513
7057
  saveQuery: () => saveQuery
6514
7058
  });
6515
7059
  function getQueriesDir(workspaceRoot) {
6516
- return path33.join(workspaceRoot, ".code-intel", "queries");
7060
+ return path35.join(workspaceRoot, ".code-intel", "queries");
6517
7061
  }
6518
7062
  function ensureQueriesDir(workspaceRoot) {
6519
7063
  const dir = getQueriesDir(workspaceRoot);
6520
- if (!fs33.existsSync(dir)) {
6521
- fs33.mkdirSync(dir, { recursive: true });
7064
+ if (!fs34.existsSync(dir)) {
7065
+ fs34.mkdirSync(dir, { recursive: true });
6522
7066
  }
6523
7067
  return dir;
6524
7068
  }
6525
7069
  function saveQuery(workspaceRoot, name, gql) {
6526
7070
  const dir = ensureQueriesDir(workspaceRoot);
6527
- const filePath = path33.join(dir, `${name}.gql`);
6528
- fs33.writeFileSync(filePath, gql, "utf-8");
7071
+ const filePath = path35.join(dir, `${name}.gql`);
7072
+ fs34.writeFileSync(filePath, gql, "utf-8");
6529
7073
  }
6530
7074
  function loadQuery(workspaceRoot, name) {
6531
7075
  const dir = getQueriesDir(workspaceRoot);
6532
- const filePath = path33.join(dir, `${name}.gql`);
6533
- if (!fs33.existsSync(filePath)) return null;
6534
- return fs33.readFileSync(filePath, "utf-8");
7076
+ const filePath = path35.join(dir, `${name}.gql`);
7077
+ if (!fs34.existsSync(filePath)) return null;
7078
+ return fs34.readFileSync(filePath, "utf-8");
6535
7079
  }
6536
7080
  function listQueries(workspaceRoot) {
6537
7081
  const dir = getQueriesDir(workspaceRoot);
6538
- if (!fs33.existsSync(dir)) return [];
6539
- const files = fs33.readdirSync(dir).filter((f) => f.endsWith(".gql"));
7082
+ if (!fs34.existsSync(dir)) return [];
7083
+ const files = fs34.readdirSync(dir).filter((f) => f.endsWith(".gql"));
6540
7084
  return files.map((f) => {
6541
- const filePath = path33.join(dir, f);
7085
+ const filePath = path35.join(dir, f);
6542
7086
  const name = f.replace(/\.gql$/, "");
6543
- const content = fs33.readFileSync(filePath, "utf-8");
6544
- const stat = fs33.statSync(filePath);
7087
+ const content = fs34.readFileSync(filePath, "utf-8");
7088
+ const stat = fs34.statSync(filePath);
6545
7089
  return {
6546
7090
  name,
6547
7091
  content,
@@ -6552,15 +7096,15 @@ function listQueries(workspaceRoot) {
6552
7096
  }
6553
7097
  function deleteQuery(workspaceRoot, name) {
6554
7098
  const dir = getQueriesDir(workspaceRoot);
6555
- const filePath = path33.join(dir, `${name}.gql`);
6556
- if (!fs33.existsSync(filePath)) return false;
6557
- fs33.unlinkSync(filePath);
7099
+ const filePath = path35.join(dir, `${name}.gql`);
7100
+ if (!fs34.existsSync(filePath)) return false;
7101
+ fs34.unlinkSync(filePath);
6558
7102
  return true;
6559
7103
  }
6560
7104
  function queryExists(workspaceRoot, name) {
6561
7105
  const dir = getQueriesDir(workspaceRoot);
6562
- const filePath = path33.join(dir, `${name}.gql`);
6563
- return fs33.existsSync(filePath);
7106
+ const filePath = path35.join(dir, `${name}.gql`);
7107
+ return fs34.existsSync(filePath);
6564
7108
  }
6565
7109
  var init_saved_queries = __esm({
6566
7110
  "src/query/saved-queries.ts"() {
@@ -6669,7 +7213,7 @@ var IGNORED_DIRS = /* @__PURE__ */ new Set([
6669
7213
  ]);
6670
7214
  function loadIgnorePatterns(workspaceRoot) {
6671
7215
  try {
6672
- const raw = fs33.readFileSync(path33.join(workspaceRoot, ".codeintelignore"), "utf-8");
7216
+ const raw = fs34.readFileSync(path35.join(workspaceRoot, ".codeintelignore"), "utf-8");
6673
7217
  const extras = /* @__PURE__ */ new Set();
6674
7218
  for (const line of raw.split("\n")) {
6675
7219
  const trimmed = line.trim();
@@ -6693,7 +7237,7 @@ var scanPhase = {
6693
7237
  function walk2(dir) {
6694
7238
  let entries;
6695
7239
  try {
6696
- entries = fs33.readdirSync(dir, { withFileTypes: true });
7240
+ entries = fs34.readdirSync(dir, { withFileTypes: true });
6697
7241
  } catch {
6698
7242
  return;
6699
7243
  }
@@ -6702,15 +7246,15 @@ var scanPhase = {
6702
7246
  if (entry.name.startsWith(".")) continue;
6703
7247
  if (IGNORED_DIRS.has(entry.name)) continue;
6704
7248
  if (extraIgnore.has(entry.name)) continue;
6705
- walk2(path33.join(dir, entry.name));
7249
+ walk2(path35.join(dir, entry.name));
6706
7250
  } else if (entry.isFile()) {
6707
7251
  const name = entry.name;
6708
7252
  if (IGNORED_FILE_SUFFIXES.some((s) => name.endsWith(s))) continue;
6709
- const ext = path33.extname(name);
7253
+ const ext = path35.extname(name);
6710
7254
  if (!extensions.has(ext)) continue;
6711
- const fullPath = path33.join(dir, name);
7255
+ const fullPath = path35.join(dir, name);
6712
7256
  try {
6713
- const stat = fs33.statSync(fullPath);
7257
+ const stat = fs34.statSync(fullPath);
6714
7258
  if (stat.size > MAX_FILE_SIZE_BYTES) continue;
6715
7259
  } catch {
6716
7260
  continue;
@@ -6737,20 +7281,20 @@ var structurePhase = {
6737
7281
  const dirs = /* @__PURE__ */ new Set();
6738
7282
  let structDone = 0;
6739
7283
  for (const filePath of context2.filePaths) {
6740
- const relativePath = path33.relative(context2.workspaceRoot, filePath);
7284
+ const relativePath = path35.relative(context2.workspaceRoot, filePath);
6741
7285
  const lang = detectLanguage(filePath);
6742
7286
  context2.graph.addNode({
6743
7287
  id: generateNodeId("file", relativePath, relativePath),
6744
7288
  kind: "file",
6745
- name: path33.basename(filePath),
7289
+ name: path35.basename(filePath),
6746
7290
  filePath: relativePath,
6747
7291
  metadata: lang ? { language: lang } : void 0
6748
7292
  });
6749
- let dir = path33.dirname(relativePath);
7293
+ let dir = path35.dirname(relativePath);
6750
7294
  while (dir && dir !== "." && dir !== "") {
6751
7295
  if (dirs.has(dir)) break;
6752
7296
  dirs.add(dir);
6753
- dir = path33.dirname(dir);
7297
+ dir = path35.dirname(dir);
6754
7298
  }
6755
7299
  structDone++;
6756
7300
  context2.onPhaseProgress?.("structure", structDone, context2.filePaths.length);
@@ -6759,7 +7303,7 @@ var structurePhase = {
6759
7303
  context2.graph.addNode({
6760
7304
  id: generateNodeId("directory", dir, dir),
6761
7305
  kind: "directory",
6762
- name: path33.basename(dir),
7306
+ name: path35.basename(dir),
6763
7307
  filePath: dir
6764
7308
  });
6765
7309
  }
@@ -6846,9 +7390,9 @@ var flowPhase = {
6846
7390
  for (const node of graph.allNodes()) {
6847
7391
  if (!["function", "method"].includes(node.kind)) continue;
6848
7392
  let score = 0;
6849
- const hasCallers = calledNodes.has(node.id);
7393
+ const hasCallers2 = calledNodes.has(node.id);
6850
7394
  const outCalls = [...graph.findEdgesFrom(node.id)].filter((e) => e.kind === "calls");
6851
- if (!hasCallers && outCalls.length > 0) score += 10;
7395
+ if (!hasCallers2 && outCalls.length > 0) score += 10;
6852
7396
  if (node.exported) score += 5;
6853
7397
  if (/^(main|handle|init|start|run|execute|process|serve|listen|bootstrap)/.test(node.name)) score += 3;
6854
7398
  if (node.filePath.includes("test") || node.filePath.includes("spec") || node.filePath.includes("__test")) score -= 20;
@@ -6870,22 +7414,22 @@ var flowPhase = {
6870
7414
  const queue = [{ nodeId: ep.id, path: [ep.id] }];
6871
7415
  const visited = /* @__PURE__ */ new Set();
6872
7416
  while (queue.length > 0 && flowCount < maxFlows) {
6873
- const { nodeId, path: path34 } = queue.shift();
6874
- if (path34.length > maxDepth) continue;
7417
+ const { nodeId, path: path36 } = queue.shift();
7418
+ if (path36.length > maxDepth) continue;
6875
7419
  const callEdges = [...graph.findEdgesFrom(nodeId)].filter((e) => e.kind === "calls").slice(0, maxBranching);
6876
- if (callEdges.length === 0 && path34.length >= 3) {
7420
+ if (callEdges.length === 0 && path36.length >= 3) {
6877
7421
  const flowId = generateNodeId("flow", ep.filePath, `flow-${flowCount}`);
6878
7422
  graph.addNode({
6879
7423
  id: flowId,
6880
7424
  kind: "flow",
6881
7425
  name: `${ep.name} flow ${flowCount}`,
6882
7426
  filePath: ep.filePath,
6883
- metadata: { steps: path34, entryPoint: ep.name }
7427
+ metadata: { steps: path36, entryPoint: ep.name }
6884
7428
  });
6885
- for (let i = 0; i < path34.length; i++) {
7429
+ for (let i = 0; i < path36.length; i++) {
6886
7430
  graph.addEdge({
6887
- id: generateEdgeId(path34[i], flowId, `step_of_${i}`),
6888
- source: path34[i],
7431
+ id: generateEdgeId(path36[i], flowId, `step_of_${i}`),
7432
+ source: path36[i],
6889
7433
  target: flowId,
6890
7434
  kind: "step_of",
6891
7435
  weight: 1,
@@ -6898,7 +7442,7 @@ var flowPhase = {
6898
7442
  for (const edge of callEdges) {
6899
7443
  if (visited.has(edge.target)) continue;
6900
7444
  visited.add(edge.target);
6901
- queue.push({ nodeId: edge.target, path: [...path34, edge.target] });
7445
+ queue.push({ nodeId: edge.target, path: [...path36, edge.target] });
6902
7446
  }
6903
7447
  }
6904
7448
  }
@@ -6916,7 +7460,7 @@ var LLMGovernanceLogger = class {
6916
7460
  }
6917
7461
  /** Path to the JSONL log file. */
6918
7462
  getLogPath() {
6919
- return process.env["CODE_INTEL_GOVERNANCE_LOG_PATH"] ?? path33.join(os12.homedir(), ".code-intel", "llm-governance.jsonl");
7463
+ return process.env["CODE_INTEL_GOVERNANCE_LOG_PATH"] ?? path35.join(os12.homedir(), ".code-intel", "llm-governance.jsonl");
6920
7464
  }
6921
7465
  /**
6922
7466
  * Append an entry to the governance log.
@@ -6932,8 +7476,8 @@ var LLMGovernanceLogger = class {
6932
7476
  ...entry
6933
7477
  };
6934
7478
  const logPath = this.getLogPath();
6935
- fs33.mkdirSync(path33.dirname(logPath), { recursive: true });
6936
- fs33.appendFileSync(logPath, JSON.stringify(full) + "\n", "utf-8");
7479
+ fs34.mkdirSync(path35.dirname(logPath), { recursive: true });
7480
+ fs34.appendFileSync(logPath, JSON.stringify(full) + "\n", "utf-8");
6937
7481
  } catch {
6938
7482
  }
6939
7483
  }
@@ -6943,7 +7487,7 @@ var LLMGovernanceLogger = class {
6943
7487
  */
6944
7488
  readLog(limit = 100) {
6945
7489
  try {
6946
- const raw = fs33.readFileSync(this.getLogPath(), "utf-8");
7490
+ const raw = fs34.readFileSync(this.getLogPath(), "utf-8");
6947
7491
  const lines = raw.split("\n").filter((l) => l.trim().length > 0).slice(-limit);
6948
7492
  return lines.map((l) => JSON.parse(l));
6949
7493
  } catch {
@@ -7257,7 +7801,7 @@ var LANG_QUERIES2 = {
7257
7801
  };
7258
7802
  function workerScriptPath() {
7259
7803
  const thisFile = fileURLToPath(import.meta.url);
7260
- return path33.join(path33.dirname(thisFile), "parse-worker.js");
7804
+ return path35.join(path35.dirname(thisFile), "parse-worker.js");
7261
7805
  }
7262
7806
  var parsePhaseParallel = {
7263
7807
  name: "parse",
@@ -7273,14 +7817,14 @@ var parsePhaseParallel = {
7273
7817
  const batch = filePaths.slice(i, i + CONCURRENCY);
7274
7818
  await Promise.all(batch.map(async (filePath) => {
7275
7819
  try {
7276
- const source = await fs33.promises.readFile(filePath, "utf-8");
7820
+ const source = await fs34.promises.readFile(filePath, "utf-8");
7277
7821
  context2.fileCache.set(filePath, source);
7278
7822
  } catch {
7279
7823
  }
7280
7824
  }));
7281
7825
  }
7282
7826
  const workerScript = workerScriptPath();
7283
- const workerScriptExists = fs33.existsSync(workerScript);
7827
+ const workerScriptExists = fs34.existsSync(workerScript);
7284
7828
  if (!workerScriptExists || workerCount === 1) {
7285
7829
  logger_default.info(`[parse-parallel] falling back to sequential (workerCount=${workerCount}, scriptExists=${workerScriptExists})`);
7286
7830
  const { parsePhase: parsePhase2 } = await Promise.resolve().then(() => (init_parse_phase(), parse_phase_exports));
@@ -7292,7 +7836,7 @@ var parsePhaseParallel = {
7292
7836
  if (!lang) continue;
7293
7837
  const source = context2.fileCache.get(filePath);
7294
7838
  if (!source) continue;
7295
- const relativePath = path33.relative(context2.workspaceRoot, filePath);
7839
+ const relativePath = path35.relative(context2.workspaceRoot, filePath);
7296
7840
  const fileNodeId = generateNodeId("file", relativePath, relativePath);
7297
7841
  const fileNode = context2.graph.getNode(fileNodeId);
7298
7842
  if (fileNode) fileNode.content = source.slice(0, 2e3);
@@ -7335,7 +7879,7 @@ var parsePhaseParallel = {
7335
7879
  symbolCount += res.nodes.length;
7336
7880
  if (res.usedTreeSitter) treeSitterCount++;
7337
7881
  else regexCount++;
7338
- const relativePath = path33.relative(context2.workspaceRoot, res.taskId);
7882
+ const relativePath = path35.relative(context2.workspaceRoot, res.taskId);
7339
7883
  const funcs = res.nodes.filter((n) => n.kind === "function" || n.kind === "method").map((n) => ({ id: n.id, startLine: n.startLine ?? 0, endLine: n.endLine })).sort((a, b) => a.startLine - b.startLine);
7340
7884
  if (funcs.length > 0) context2.fileFunctionIndex.set(relativePath, funcs);
7341
7885
  parseDone++;
@@ -7362,7 +7906,7 @@ init_id_generator();
7362
7906
  init_logger();
7363
7907
  function workerScriptPath2() {
7364
7908
  const thisFile = fileURLToPath(import.meta.url);
7365
- return path33.join(path33.dirname(thisFile), "resolve-worker.js");
7909
+ return path35.join(path35.dirname(thisFile), "resolve-worker.js");
7366
7910
  }
7367
7911
  var resolvePhaseParallel = {
7368
7912
  name: "resolve",
@@ -7374,11 +7918,11 @@ var resolvePhaseParallel = {
7374
7918
  const fileFunctionIndex = context2.fileFunctionIndex ?? /* @__PURE__ */ new Map();
7375
7919
  const fileIndex = {};
7376
7920
  for (const fp of filePaths) {
7377
- const rel = path33.relative(workspaceRoot, fp);
7921
+ const rel = path35.relative(workspaceRoot, fp);
7378
7922
  fileIndex[rel] = fp;
7379
7923
  const noExt = rel.replace(/\.\w+$/, "");
7380
7924
  if (!fileIndex[noExt]) fileIndex[noExt] = fp;
7381
- const base = path33.basename(rel, path33.extname(rel));
7925
+ const base = path35.basename(rel, path35.extname(rel));
7382
7926
  if (!fileIndex[base]) fileIndex[base] = fp;
7383
7927
  }
7384
7928
  const symbolIndex = {};
@@ -7392,7 +7936,7 @@ var resolvePhaseParallel = {
7392
7936
  }
7393
7937
  const snapshot = { symbolIndex, fileSymbolIndex, fileIndex, workspaceRoot };
7394
7938
  const workerScript = workerScriptPath2();
7395
- const workerScriptExists = fs33.existsSync(workerScript);
7939
+ const workerScriptExists = fs34.existsSync(workerScript);
7396
7940
  const workerCount = parseInt(process.env["PARSE_WORKERS"] ?? "", 10) || Math.max(1, os12.cpus().length - 1);
7397
7941
  if (!workerScriptExists || workerCount === 1) {
7398
7942
  logger_default.info(`[resolve-parallel] falling back to sequential`);
@@ -7405,7 +7949,7 @@ var resolvePhaseParallel = {
7405
7949
  if (!lang) continue;
7406
7950
  const source = fileCache.get(filePath);
7407
7951
  if (!source) continue;
7408
- const relativePath = path33.relative(workspaceRoot, filePath);
7952
+ const relativePath = path35.relative(workspaceRoot, filePath);
7409
7953
  const fileNodeId = generateNodeId("file", relativePath, relativePath);
7410
7954
  const funcList = fileFunctionIndex.get(relativePath) ?? [];
7411
7955
  tasks.push({ taskId: filePath, filePath, relativePath, fileNodeId, source, funcList });
@@ -7528,7 +8072,7 @@ init_embedder();
7528
8072
  async function hybridSearch(graph, query, limit, options = {}) {
7529
8073
  const { vectorDbPath, bm25Limit = 50, vectorLimit = 50 } = options;
7530
8074
  const bm25Promise = Promise.resolve(textSearch(graph, query, bm25Limit));
7531
- const hasVectorDb = Boolean(vectorDbPath && fs33.existsSync(vectorDbPath));
8075
+ const hasVectorDb = Boolean(vectorDbPath && fs34.existsSync(vectorDbPath));
7532
8076
  if (!hasVectorDb) {
7533
8077
  const bm25Results2 = await bm25Promise;
7534
8078
  return {
@@ -7595,8 +8139,8 @@ async function queryGroup(group, query, limit = 20) {
7595
8139
  for (const member of group.members) {
7596
8140
  const regEntry = registry.find((r) => r.name === member.registryName);
7597
8141
  if (!regEntry) continue;
7598
- const dbPath = path33.join(regEntry.path, ".code-intel", "graph.db");
7599
- if (!fs33.existsSync(dbPath)) continue;
8142
+ const dbPath = path35.join(regEntry.path, ".code-intel", "graph.db");
8143
+ if (!fs34.existsSync(dbPath)) continue;
7600
8144
  const graph = createKnowledgeGraph();
7601
8145
  const db = new DbManager(dbPath);
7602
8146
  try {
@@ -7638,7 +8182,7 @@ var STUCK_THRESHOLD_MINUTES = 30;
7638
8182
  var JobsDB = class {
7639
8183
  db;
7640
8184
  constructor(dbPath) {
7641
- fs33.mkdirSync(path33.dirname(dbPath), { recursive: true });
8185
+ fs34.mkdirSync(path35.dirname(dbPath), { recursive: true });
7642
8186
  this.db = new Database(dbPath);
7643
8187
  this.db.pragma("journal_mode = WAL");
7644
8188
  this.db.pragma("foreign_keys = ON");
@@ -7780,7 +8324,7 @@ var JobsDB = class {
7780
8324
  }
7781
8325
  };
7782
8326
  function getJobsDBPath() {
7783
- return path33.join(os12.homedir(), ".code-intel", "jobs.db");
8327
+ return path35.join(os12.homedir(), ".code-intel", "jobs.db");
7784
8328
  }
7785
8329
  var _jobsDB = null;
7786
8330
  function getOrCreateJobsDB() {
@@ -7872,7 +8416,7 @@ var BACKUP_VERSION = "1.0";
7872
8416
  var ALGORITHM = "aes-256-gcm";
7873
8417
  var IV_LENGTH = 16;
7874
8418
  function getBackupDir() {
7875
- return path33.join(os12.homedir(), ".code-intel", "backups");
8419
+ return path35.join(os12.homedir(), ".code-intel", "backups");
7876
8420
  }
7877
8421
  function getBackupKey() {
7878
8422
  const keyHex = process.env["CODE_INTEL_BACKUP_KEY"];
@@ -7903,30 +8447,30 @@ var BackupService = class {
7903
8447
  constructor(backupDir) {
7904
8448
  this.backupDir = backupDir ?? getBackupDir();
7905
8449
  this.key = getBackupKey();
7906
- fs33.mkdirSync(this.backupDir, { recursive: true });
8450
+ fs34.mkdirSync(this.backupDir, { recursive: true });
7907
8451
  }
7908
8452
  /**
7909
8453
  * Create a backup for a repository.
7910
8454
  * Returns the backup entry.
7911
8455
  */
7912
8456
  createBackup(repoPath) {
7913
- const codeIntelDir = path33.join(repoPath, ".code-intel");
8457
+ const codeIntelDir = path35.join(repoPath, ".code-intel");
7914
8458
  const id = v4();
7915
8459
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
7916
8460
  const filesToBackup = [];
7917
8461
  const candidates = ["graph.db", "vector.db", "meta.json"];
7918
8462
  for (const f of candidates) {
7919
- const fp = path33.join(codeIntelDir, f);
7920
- if (fs33.existsSync(fp)) {
8463
+ const fp = path35.join(codeIntelDir, f);
8464
+ if (fs34.existsSync(fp)) {
7921
8465
  filesToBackup.push({ name: f, localPath: fp });
7922
8466
  }
7923
8467
  }
7924
- const registryPath = path33.join(os12.homedir(), ".code-intel", "registry.json");
7925
- if (fs33.existsSync(registryPath)) {
8468
+ const registryPath = path35.join(os12.homedir(), ".code-intel", "registry.json");
8469
+ if (fs34.existsSync(registryPath)) {
7926
8470
  filesToBackup.push({ name: "registry.json", localPath: registryPath });
7927
8471
  }
7928
- const usersDbPath = path33.join(os12.homedir(), ".code-intel", "users.db");
7929
- if (fs33.existsSync(usersDbPath)) {
8472
+ const usersDbPath = path35.join(os12.homedir(), ".code-intel", "users.db");
8473
+ if (fs34.existsSync(usersDbPath)) {
7930
8474
  filesToBackup.push({ name: "users.db", localPath: usersDbPath });
7931
8475
  }
7932
8476
  if (filesToBackup.length === 0) {
@@ -7937,7 +8481,7 @@ var BackupService = class {
7937
8481
  createdAt,
7938
8482
  version: BACKUP_VERSION,
7939
8483
  files: filesToBackup.map((f) => {
7940
- const data = fs33.readFileSync(f.localPath);
8484
+ const data = fs34.readFileSync(f.localPath);
7941
8485
  return {
7942
8486
  name: f.name,
7943
8487
  sha256: crypto5.createHash("sha256").update(data).digest("hex"),
@@ -7951,7 +8495,7 @@ var BackupService = class {
7951
8495
  manifestLenBuf.writeUInt32BE(manifestBuf.length, 0);
7952
8496
  parts.push(manifestLenBuf, manifestBuf);
7953
8497
  for (const f of filesToBackup) {
7954
- const data = fs33.readFileSync(f.localPath);
8498
+ const data = fs34.readFileSync(f.localPath);
7955
8499
  const nameBuf = Buffer.from(f.name, "utf-8");
7956
8500
  const nameLenBuf = Buffer.alloc(2);
7957
8501
  nameLenBuf.writeUInt16BE(nameBuf.length, 0);
@@ -7962,8 +8506,8 @@ var BackupService = class {
7962
8506
  const plaintext = Buffer.concat(parts);
7963
8507
  const encrypted = encryptBuffer(plaintext, this.key);
7964
8508
  const backupFileName = `backup-${id}.cib`;
7965
- const backupPath = path33.join(this.backupDir, backupFileName);
7966
- fs33.writeFileSync(backupPath, encrypted);
8509
+ const backupPath = path35.join(this.backupDir, backupFileName);
8510
+ fs34.writeFileSync(backupPath, encrypted);
7967
8511
  const entry = {
7968
8512
  id,
7969
8513
  createdAt,
@@ -7990,9 +8534,9 @@ var BackupService = class {
7990
8534
  async uploadToS3(entry) {
7991
8535
  const cfg = getS3Config();
7992
8536
  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.");
7993
- const fileName = path33.basename(entry.path);
8537
+ const fileName = path35.basename(entry.path);
7994
8538
  const s3Key = `${cfg.prefix}${fileName}`;
7995
- const body = fs33.readFileSync(entry.path);
8539
+ const body = fs34.readFileSync(entry.path);
7996
8540
  const result = await s3Request({ method: "PUT", cfg, key: s3Key, body });
7997
8541
  if (result.statusCode < 200 || result.statusCode >= 300) {
7998
8542
  throw new Error(`S3 upload failed (HTTP ${result.statusCode}): ${result.body.slice(0, 200)}`);
@@ -8009,8 +8553,8 @@ var BackupService = class {
8009
8553
  if (result.statusCode < 200 || result.statusCode >= 300) {
8010
8554
  throw new Error(`S3 download failed (HTTP ${result.statusCode}): ${result.body.slice(0, 200)}`);
8011
8555
  }
8012
- fs33.mkdirSync(path33.dirname(destPath), { recursive: true });
8013
- fs33.writeFileSync(destPath, Buffer.from(result.body, "binary"));
8556
+ fs34.mkdirSync(path35.dirname(destPath), { recursive: true });
8557
+ fs34.writeFileSync(destPath, Buffer.from(result.body, "binary"));
8014
8558
  }
8015
8559
  /**
8016
8560
  * List backup objects in S3 with the configured prefix.
@@ -8056,10 +8600,10 @@ var BackupService = class {
8056
8600
  if (!entry) {
8057
8601
  throw new Error(`Backup "${backupId}" not found.`);
8058
8602
  }
8059
- if (!fs33.existsSync(entry.path)) {
8603
+ if (!fs34.existsSync(entry.path)) {
8060
8604
  throw new Error(`Backup file not found at: ${entry.path}`);
8061
8605
  }
8062
- const encrypted = fs33.readFileSync(entry.path);
8606
+ const encrypted = fs34.readFileSync(entry.path);
8063
8607
  let plaintext;
8064
8608
  try {
8065
8609
  plaintext = decryptBuffer(encrypted, this.key);
@@ -8073,8 +8617,8 @@ var BackupService = class {
8073
8617
  offset += manifestLen;
8074
8618
  const manifest = JSON.parse(manifestStr);
8075
8619
  const restoreBase = targetRepoPath ?? entry.repoPath;
8076
- const codeIntelDir = path33.join(restoreBase, ".code-intel");
8077
- fs33.mkdirSync(codeIntelDir, { recursive: true });
8620
+ const codeIntelDir = path35.join(restoreBase, ".code-intel");
8621
+ fs34.mkdirSync(codeIntelDir, { recursive: true });
8078
8622
  for (const fileEntry of manifest.files) {
8079
8623
  const nameLen = plaintext.readUInt16BE(offset);
8080
8624
  offset += 2;
@@ -8091,18 +8635,18 @@ var BackupService = class {
8091
8635
  }
8092
8636
  let destPath;
8093
8637
  if (name === "registry.json" || name === "users.db") {
8094
- destPath = path33.join(os12.homedir(), ".code-intel", name);
8638
+ destPath = path35.join(os12.homedir(), ".code-intel", name);
8095
8639
  } else {
8096
- destPath = path33.join(codeIntelDir, name);
8640
+ destPath = path35.join(codeIntelDir, name);
8097
8641
  }
8098
- fs33.writeFileSync(destPath, data);
8642
+ fs34.writeFileSync(destPath, data);
8099
8643
  }
8100
8644
  }
8101
8645
  /**
8102
8646
  * Apply retention policy: keep N daily, M weekly, L monthly backups.
8103
8647
  */
8104
8648
  applyRetention(options = { daily: 7, weekly: 4, monthly: 12 }) {
8105
- const entries = this._loadIndex().filter((e) => fs33.existsSync(e.path)).sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
8649
+ const entries = this._loadIndex().filter((e) => fs34.existsSync(e.path)).sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
8106
8650
  const keep = /* @__PURE__ */ new Set();
8107
8651
  const now = /* @__PURE__ */ new Date();
8108
8652
  const dailyCutoff = new Date(now);
@@ -8132,7 +8676,7 @@ var BackupService = class {
8132
8676
  for (const e of entries) {
8133
8677
  if (!keep.has(e.id)) {
8134
8678
  try {
8135
- fs33.unlinkSync(e.path);
8679
+ fs34.unlinkSync(e.path);
8136
8680
  deleted++;
8137
8681
  } catch {
8138
8682
  }
@@ -8144,17 +8688,17 @@ var BackupService = class {
8144
8688
  }
8145
8689
  // ── Index helpers ──────────────────────────────────────────────────────────
8146
8690
  _indexPath() {
8147
- return path33.join(this.backupDir, "index.json");
8691
+ return path35.join(this.backupDir, "index.json");
8148
8692
  }
8149
8693
  _loadIndex() {
8150
8694
  try {
8151
- return JSON.parse(fs33.readFileSync(this._indexPath(), "utf-8"));
8695
+ return JSON.parse(fs34.readFileSync(this._indexPath(), "utf-8"));
8152
8696
  } catch {
8153
8697
  return [];
8154
8698
  }
8155
8699
  }
8156
8700
  _saveIndex(entries) {
8157
- fs33.writeFileSync(this._indexPath(), JSON.stringify(entries, null, 2));
8701
+ fs34.writeFileSync(this._indexPath(), JSON.stringify(entries, null, 2));
8158
8702
  }
8159
8703
  _appendIndex(entry) {
8160
8704
  const entries = this._loadIndex();
@@ -8795,11 +9339,11 @@ var openApiSpec = {
8795
9339
  };
8796
9340
 
8797
9341
  // src/http/app.ts
8798
- var __dirname$1 = path33.dirname(fileURLToPath(import.meta.url));
9342
+ var __dirname$1 = path35.dirname(fileURLToPath(import.meta.url));
8799
9343
  var WEB_DIST = (() => {
8800
- const bundled = path33.resolve(__dirname$1, "..", "web");
8801
- if (fs33.existsSync(bundled)) return bundled;
8802
- return path33.resolve(__dirname$1, "..", "..", "..", "web", "dist");
9344
+ const bundled = path35.resolve(__dirname$1, "..", "web");
9345
+ if (fs34.existsSync(bundled)) return bundled;
9346
+ return path35.resolve(__dirname$1, "..", "..", "..", "web", "dist");
8803
9347
  })();
8804
9348
  function getAllowedOrigins() {
8805
9349
  const env = process.env["CODE_INTEL_CORS_ORIGINS"];
@@ -9330,8 +9874,8 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
9330
9874
  const registry = loadRegistry();
9331
9875
  const entry = registry.find((r) => r.name === requestedRepo || r.path === requestedRepo);
9332
9876
  if (!entry) return null;
9333
- const dbPath = path33.join(entry.path, ".code-intel", "graph.db");
9334
- if (!fs33.existsSync(dbPath)) return null;
9877
+ const dbPath = path35.join(entry.path, ".code-intel", "graph.db");
9878
+ if (!fs34.existsSync(dbPath)) return null;
9335
9879
  const repoGraph = createKnowledgeGraph();
9336
9880
  const db = new DbManager(dbPath);
9337
9881
  try {
@@ -9418,7 +9962,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
9418
9962
  return;
9419
9963
  }
9420
9964
  try {
9421
- const content = fs33.readFileSync(file_path, "utf-8");
9965
+ const content = fs34.readFileSync(file_path, "utf-8");
9422
9966
  res.json({ content });
9423
9967
  } catch {
9424
9968
  res.status(404).json({ error: { code: ErrorCodes.NOT_FOUND, message: "File not found" } });
@@ -9676,8 +10220,8 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
9676
10220
  for (const member of group.members) {
9677
10221
  const regEntry = registry.find((r) => r.name === member.registryName);
9678
10222
  if (!regEntry) continue;
9679
- const dbPath = path33.join(regEntry.path, ".code-intel", "graph.db");
9680
- if (!fs33.existsSync(dbPath)) continue;
10223
+ const dbPath = path35.join(regEntry.path, ".code-intel", "graph.db");
10224
+ if (!fs34.existsSync(dbPath)) continue;
9681
10225
  const db = new DbManager(dbPath);
9682
10226
  try {
9683
10227
  await db.init();
@@ -9703,8 +10247,8 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
9703
10247
  let nodeCount = 0;
9704
10248
  let edgeCount = 0;
9705
10249
  if (regEntry) {
9706
- const dbPath = path33.join(regEntry.path, ".code-intel", "graph.db");
9707
- if (fs33.existsSync(dbPath)) {
10250
+ const dbPath = path35.join(regEntry.path, ".code-intel", "graph.db");
10251
+ if (fs34.existsSync(dbPath)) {
9708
10252
  try {
9709
10253
  const db = new DbManager(dbPath);
9710
10254
  await db.init();
@@ -9753,14 +10297,14 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
9753
10297
  });
9754
10298
  return;
9755
10299
  }
9756
- let rawResolved = path33.normalize(file);
9757
- if (!path33.isAbsolute(rawResolved) && workspaceRoot) {
9758
- rawResolved = path33.join(workspaceRoot, rawResolved);
10300
+ let rawResolved = path35.normalize(file);
10301
+ if (!path35.isAbsolute(rawResolved) && workspaceRoot) {
10302
+ rawResolved = path35.join(workspaceRoot, rawResolved);
9759
10303
  }
9760
- const resolvedFile = path33.resolve(rawResolved);
10304
+ const resolvedFile = path35.resolve(rawResolved);
9761
10305
  function isInsideDir(fileAbs, dir) {
9762
- const rel = path33.relative(path33.resolve(dir), fileAbs);
9763
- return !rel.startsWith("..") && !path33.isAbsolute(rel);
10306
+ const rel = path35.relative(path35.resolve(dir), fileAbs);
10307
+ return !rel.startsWith("..") && !path35.isAbsolute(rel);
9764
10308
  }
9765
10309
  if (workspaceRoot) {
9766
10310
  if (!isInsideDir(resolvedFile, workspaceRoot)) {
@@ -9797,7 +10341,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
9797
10341
  }
9798
10342
  let fileContent;
9799
10343
  try {
9800
- fileContent = fs33.readFileSync(resolvedFile, "utf-8");
10344
+ fileContent = fs34.readFileSync(resolvedFile, "utf-8");
9801
10345
  } catch {
9802
10346
  res.status(404).json({
9803
10347
  error: {
@@ -9828,7 +10372,7 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
9828
10372
  const contextStart = Math.max(1, startLine - 20);
9829
10373
  const contextEnd = Math.min(lines.length, endLine + 20);
9830
10374
  const content = lines.slice(contextStart - 1, contextEnd).join("\n");
9831
- const ext = path33.extname(resolvedFile).toLowerCase();
10375
+ const ext = path35.extname(resolvedFile).toLowerCase();
9832
10376
  const languageMap = {
9833
10377
  ".ts": "typescript",
9834
10378
  ".tsx": "typescript",
@@ -9963,10 +10507,10 @@ function createApp(graph, repoName, workspaceRoot, watcherState) {
9963
10507
  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() } });
9964
10508
  }
9965
10509
  });
9966
- if (fs33.existsSync(WEB_DIST)) {
10510
+ if (fs34.existsSync(WEB_DIST)) {
9967
10511
  app.use(express.static(WEB_DIST));
9968
10512
  app.get("/{*path}", (_req, res) => {
9969
- res.sendFile(path33.join(WEB_DIST, "index.html"));
10513
+ res.sendFile(path35.join(WEB_DIST, "index.html"));
9970
10514
  });
9971
10515
  }
9972
10516
  app.use("/admin", requireRole("admin"));
@@ -10495,22 +11039,22 @@ function suggestTests(graph, symbolName) {
10495
11039
  const callPaths = [];
10496
11040
  const pathQueue = [{ id: targetId, path: [symbolName], depth: 0 }];
10497
11041
  while (pathQueue.length > 0 && callPaths.length < 5) {
10498
- const { id, path: path34, depth } = pathQueue.shift();
10499
- let hasCallers = false;
11042
+ const { id, path: path36, depth } = pathQueue.shift();
11043
+ let hasCallers2 = false;
10500
11044
  for (const edge of graph.findEdgesTo(id)) {
10501
11045
  if (edge.kind !== "calls") continue;
10502
11046
  const callerNode = graph.getNode(edge.source);
10503
11047
  if (!callerNode) continue;
10504
- hasCallers = true;
10505
- const newPath = [callerNode.name, ...path34];
11048
+ hasCallers2 = true;
11049
+ const newPath = [callerNode.name, ...path36];
10506
11050
  if (depth + 1 >= 3 || callPaths.length >= 5) {
10507
11051
  if (callPaths.length < 5) callPaths.push(newPath);
10508
11052
  continue;
10509
11053
  }
10510
11054
  pathQueue.push({ id: edge.source, path: newPath, depth: depth + 1 });
10511
11055
  }
10512
- if (!hasCallers && path34.length > 1) {
10513
- callPaths.push(path34);
11056
+ if (!hasCallers2 && path36.length > 1) {
11057
+ callPaths.push(path36);
10514
11058
  }
10515
11059
  }
10516
11060
  if (callPaths.length === 0) {
@@ -10985,6 +11529,70 @@ function createMcpServer(graph, repoName, workspaceRoot) {
10985
11529
  },
10986
11530
  required: ["cluster"]
10987
11531
  }
11532
+ },
11533
+ {
11534
+ name: "deprecated_usage",
11535
+ description: "Find usages of deprecated APIs in the codebase",
11536
+ inputSchema: {
11537
+ type: "object",
11538
+ properties: {
11539
+ scope: { type: "string", description: "Directory scope filter" },
11540
+ ..._tokenProp
11541
+ }
11542
+ }
11543
+ },
11544
+ {
11545
+ name: "complexity_hotspots",
11546
+ description: "Ranked list of functions/methods by cyclomatic complexity. Useful for identifying refactoring candidates.",
11547
+ inputSchema: {
11548
+ type: "object",
11549
+ properties: {
11550
+ scope: { type: "string", description: "Limit to a file path prefix (optional)" },
11551
+ limit: { type: "number", description: "Maximum number of results (default: 20)" },
11552
+ ..._tokenProp
11553
+ }
11554
+ }
11555
+ },
11556
+ {
11557
+ name: "coverage_gaps",
11558
+ description: "Find exported symbols with no test coverage, ranked by blast radius. Useful for prioritizing test writing.",
11559
+ inputSchema: {
11560
+ type: "object",
11561
+ properties: {
11562
+ scope: { type: "string", description: "Limit to a file path prefix (optional)" },
11563
+ limit: { type: "number", description: "Maximum number of untested results to return (default: 20)" },
11564
+ ..._tokenProp
11565
+ }
11566
+ }
11567
+ },
11568
+ {
11569
+ name: "secrets",
11570
+ description: "Scan the knowledge graph for hardcoded secrets: API keys, passwords, tokens, private keys, high-entropy strings",
11571
+ inputSchema: {
11572
+ type: "object",
11573
+ properties: {
11574
+ scope: { type: "string", description: "Limit scan to files under this path prefix" },
11575
+ includeTestFiles: { type: "boolean", description: "Include test/spec/fixture files (default: false)" },
11576
+ ..._tokenProp
11577
+ }
11578
+ }
11579
+ },
11580
+ {
11581
+ name: "vulnerability_scan",
11582
+ description: "Scan the knowledge graph for OWASP vulnerabilities: SQL injection, XSS, SSRF, path traversal, command injection",
11583
+ inputSchema: {
11584
+ type: "object",
11585
+ properties: {
11586
+ scope: { type: "string", description: "Limit scan to files under this path prefix" },
11587
+ types: {
11588
+ type: "array",
11589
+ items: { type: "string", enum: ["SQL_INJECTION", "XSS", "SSRF", "PATH_TRAVERSAL", "COMMAND_INJECTION"] },
11590
+ description: "Vulnerability types to detect (default: all)"
11591
+ },
11592
+ severity: { type: "string", description: "Minimum severity to report: HIGH|MEDIUM|LOW (default: LOW)" },
11593
+ ..._tokenProp
11594
+ }
11595
+ }
10988
11596
  }
10989
11597
  ]
10990
11598
  }));
@@ -11408,7 +12016,7 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
11408
12016
  for (const { filePath: changedFile, changedLines } of changedFiles) {
11409
12017
  for (const node of graph.allNodes()) {
11410
12018
  if (!node.filePath) continue;
11411
- const normNode = node.filePath.replace(repoRoot + "/", "").replace(repoRoot + path33.sep, "");
12019
+ const normNode = node.filePath.replace(repoRoot + "/", "").replace(repoRoot + path35.sep, "");
11412
12020
  const normChanged = changedFile.replace(/^a\/|^b\//, "");
11413
12021
  if (!normNode.endsWith(normChanged) && !normChanged.endsWith(normNode)) continue;
11414
12022
  if (node.startLine !== void 0 && node.endLine !== void 0) {
@@ -11681,6 +12289,63 @@ async function dispatchTool(name, a, graph, repoName, workspaceRoot) {
11681
12289
  const result = summarizeCluster(graph, cluster);
11682
12290
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
11683
12291
  }
12292
+ case "deprecated_usage": {
12293
+ const scope = a.scope;
12294
+ const { DeprecatedDetector: DeprecatedDetector2 } = await Promise.resolve().then(() => (init_deprecated_detector(), deprecated_detector_exports));
12295
+ const detector = new DeprecatedDetector2();
12296
+ detector.tagDeprecated(graph);
12297
+ const findings = detector.detect(graph, scope);
12298
+ return { content: [{ type: "text", text: JSON.stringify({ findings, total: findings.length }, null, 2) }] };
12299
+ }
12300
+ // ── complexity_hotspots ────────────────────────────────────────────────
12301
+ case "complexity_hotspots": {
12302
+ const { computeComplexity: computeComplexity2 } = await Promise.resolve().then(() => (init_complexity(), complexity_exports));
12303
+ const scope = a.scope;
12304
+ const limit = typeof a.limit === "number" ? a.limit : 20;
12305
+ const hotspots = computeComplexity2(graph, scope).slice(0, limit);
12306
+ return { content: [{ type: "text", text: JSON.stringify({ hotspots, total: hotspots.length }, null, 2) }] };
12307
+ }
12308
+ // ── coverage_gaps ──────────────────────────────────────────────────────
12309
+ case "coverage_gaps": {
12310
+ const { computeCoverage: computeCoverage2 } = await Promise.resolve().then(() => (init_test_coverage(), test_coverage_exports));
12311
+ const scope = a.scope;
12312
+ const limit = typeof a.limit === "number" ? a.limit : 20;
12313
+ const summary = computeCoverage2(graph, scope);
12314
+ const untestedByRisk = summary.untestedByRisk.slice(0, limit);
12315
+ return {
12316
+ content: [{
12317
+ type: "text",
12318
+ text: JSON.stringify({
12319
+ untestedByRisk,
12320
+ coveragePct: summary.coveragePct,
12321
+ totalExported: summary.totalExported,
12322
+ testedExported: summary.testedExported
12323
+ }, null, 2)
12324
+ }]
12325
+ };
12326
+ }
12327
+ // ── secrets ────────────────────────────────────────────────────────────
12328
+ case "secrets": {
12329
+ const { SecretScanner: SecretScanner2 } = await Promise.resolve().then(() => (init_secret_scanner(), secret_scanner_exports));
12330
+ const scanner = new SecretScanner2();
12331
+ const scope = a.scope;
12332
+ const includeTestFiles = a.includeTestFiles ?? false;
12333
+ const findings = scanner.scan(graph, { scope, includeTestFiles });
12334
+ return { content: [{ type: "text", text: JSON.stringify({ findings, total: findings.length }, null, 2) }] };
12335
+ }
12336
+ // ── vulnerability_scan ─────────────────────────────────────────────────
12337
+ case "vulnerability_scan": {
12338
+ const { VulnerabilityDetector: VulnerabilityDetector2 } = await Promise.resolve().then(() => (init_vulnerability_detector(), vulnerability_detector_exports));
12339
+ const detector = new VulnerabilityDetector2();
12340
+ const scope = a.scope;
12341
+ const types = a.types;
12342
+ const minSev = (a.severity ?? "LOW").toUpperCase();
12343
+ const sevRank = { HIGH: 3, MEDIUM: 2, LOW: 1 };
12344
+ const minRank = sevRank[minSev] ?? 1;
12345
+ let findings = detector.detect(graph, { scope, types });
12346
+ findings = findings.filter((f) => (sevRank[f.severity] ?? 1) >= minRank);
12347
+ return { content: [{ type: "text", text: JSON.stringify({ findings, total: findings.length }, null, 2) }] };
12348
+ }
11684
12349
  default:
11685
12350
  return { content: [{ type: "text", text: `Unknown tool: ${name}` }] };
11686
12351
  }
@@ -11767,20 +12432,20 @@ function parseDiff(diffText) {
11767
12432
  // src/cli/main.ts
11768
12433
  init_metadata();
11769
12434
  async function writeSkillFiles(graph, workspaceRoot, projectName) {
11770
- const outputDir = path33.join(workspaceRoot, ".claude", "skills", "code-intel");
12435
+ const outputDir = path35.join(workspaceRoot, ".claude", "skills", "code-intel");
11771
12436
  const areas = buildAreaMap(graph, workspaceRoot);
11772
12437
  if (areas.length === 0) return { skills: [], outputDir };
11773
- fs33.rmSync(outputDir, { recursive: true, force: true });
11774
- fs33.mkdirSync(outputDir, { recursive: true });
12438
+ fs34.rmSync(outputDir, { recursive: true, force: true });
12439
+ fs34.mkdirSync(outputDir, { recursive: true });
11775
12440
  const skills = [];
11776
12441
  const usedNames = /* @__PURE__ */ new Set();
11777
12442
  for (const area of areas) {
11778
12443
  const kebab = uniqueKebab(area.label, usedNames);
11779
12444
  usedNames.add(kebab);
11780
12445
  const content = renderSkill(area, projectName, kebab);
11781
- const dir = path33.join(outputDir, kebab);
11782
- fs33.mkdirSync(dir, { recursive: true });
11783
- fs33.writeFileSync(path33.join(dir, "SKILL.md"), content, "utf-8");
12446
+ const dir = path35.join(outputDir, kebab);
12447
+ fs34.mkdirSync(dir, { recursive: true });
12448
+ fs34.writeFileSync(path35.join(dir, "SKILL.md"), content, "utf-8");
11784
12449
  skills.push({ name: kebab, label: area.label, symbolCount: area.nodes.length, fileCount: area.files.size });
11785
12450
  }
11786
12451
  return { skills, outputDir };
@@ -11960,11 +12625,20 @@ var BLOCK_START = "<!-- code-intel:start -->";
11960
12625
  var BLOCK_END = "<!-- code-intel:end -->";
11961
12626
  function writeContextFiles(workspaceRoot, projectName, stats, skills) {
11962
12627
  const block = buildBlock(projectName, stats, skills);
11963
- upsertFile(path33.join(workspaceRoot, "AGENTS.md"), block, "AGENTS.md");
11964
- upsertFile(path33.join(workspaceRoot, "CLAUDE.md"), block, "CLAUDE.md");
12628
+ upsertFile(path35.join(workspaceRoot, "AGENTS.md"), block, "AGENTS.md");
12629
+ upsertFile(path35.join(workspaceRoot, "CLAUDE.md"), block, "CLAUDE.md");
12630
+ const githubDir = path35.join(workspaceRoot, ".github");
12631
+ if (!fs34.existsSync(githubDir)) fs34.mkdirSync(githubDir, { recursive: true });
12632
+ upsertFile(path35.join(githubDir, "copilot-instructions.md"), block, "copilot-instructions.md");
12633
+ const cursorDir = path35.join(workspaceRoot, ".cursor", "rules");
12634
+ if (!fs34.existsSync(cursorDir)) fs34.mkdirSync(cursorDir, { recursive: true });
12635
+ upsertFile(path35.join(cursorDir, "code-intel.mdc"), block, "code-intel.mdc");
12636
+ const kiroDir = path35.join(workspaceRoot, ".kiro", "steering");
12637
+ if (!fs34.existsSync(kiroDir)) fs34.mkdirSync(kiroDir, { recursive: true });
12638
+ upsertFile(path35.join(kiroDir, "code-intel.md"), block, "code-intel.md");
11965
12639
  }
11966
12640
  function buildBlock(projectName, stats, skills) {
11967
- const skillRows = skills.map(
12641
+ const skillTableRows = skills.map(
11968
12642
  (s) => `| Work in \`${s.label}\` (${s.symbolCount} symbols) | \`.claude/skills/code-intel/${s.name}/SKILL.md\` |`
11969
12643
  ).join("\n");
11970
12644
  const skillTable = `| Task | Skill file |
@@ -11972,7 +12646,15 @@ function buildBlock(projectName, stats, skills) {
11972
12646
  | Understand architecture / "How does X work?" | Load \`code-intel-exploring\` skill |
11973
12647
  | Blast radius / "What breaks if I change X?" | Load \`code-intel-impact\` skill |
11974
12648
  | Debugging / "Why is X failing?" | Load \`code-intel-debugging\` skill |
11975
- ${skillRows ? skillRows + "\n" : ""}`;
12649
+ ${skillTableRows ? skillTableRows + "\n" : ""}`;
12650
+ const skillLoadInstructions = skills.length > 0 ? `
12651
+ ## When to Load a Skill
12652
+
12653
+ Before working deeply in a subsystem, **load the matching skill file** listed above.
12654
+ Each skill gives you symbol maps, key entry points, and safe-change guidance for that area.
12655
+
12656
+ ${skills.map((s) => `- Working in **${s.label}**? \u2192 Load \`.claude/skills/code-intel/${s.name}/SKILL.md\``).join("\n")}
12657
+ ` : "";
11976
12658
  return `${BLOCK_START}
11977
12659
  # Code Intelligence \u2014 ${projectName}
11978
12660
 
@@ -11983,39 +12665,114 @@ Indexed: **${stats.nodes.toLocaleString()} nodes** | **${stats.edges.toLocaleStr
11983
12665
 
11984
12666
  > Index stale? Re-run: \`code-intel analyze\`
11985
12667
 
11986
- ## Always Do
12668
+ ## Mandatory Rules \u2014 ALL Agents (Amp, Claude Code, Codex, Copilot, Cursor, Aider, Gemini, Kiro, Trae, Hermes, Factory, OpenCode, Pi, Antigravity, OpenClaw, and others)
11987
12669
 
11988
- - **Before editing any symbol**, run \`code-intel impact <symbol>\` to review its blast radius.
11989
- - **Before committing**, verify scope with \`code-intel inspect <symbol>\`.
11990
- - Use \`code-intel search "<concept>"\` to find related symbols instead of grepping.
11991
- - Warn the user if impact shows \u2265 5 direct callers (**HIGH risk**).
12670
+ These rules apply to **every coding agent or AI assistant** working in this repository.
11992
12671
 
11993
- ## Never Do
12672
+ ### Before Implementing Any Change
12673
+ 1. Run \`code-intel search "<concept>"\` \u2014 find existing symbols related to the task.
12674
+ 2. Run \`code-intel inspect <symbol>\` \u2014 understand callers, callees, and imports.
12675
+ 3. Run \`code-intel impact <symbol>\` \u2014 check blast radius before touching anything.
12676
+ 4. **If impact shows \u2265 5 direct callers \u2192 warn the user: HIGH risk.**
11994
12677
 
11995
- - NEVER rename symbols with find-and-replace \u2014 use \`code-intel inspect\` to find all usages first.
11996
- - NEVER ignore impact warnings \u2014 always report blast radius to the user.
12678
+ ### While Implementing / Fixing a Bug
11997
12679
  - NEVER open a file cold \u2014 always \`code-intel search\` first.
11998
12680
  - NEVER grep for symbols \u2014 use \`code-intel search\` instead.
12681
+ - NEVER rename symbols with find-and-replace \u2014 use \`code-intel inspect\` to find all usages first.
12682
+ - Use \`code-intel inspect <symbol>\` to understand a function's callers/callees before modifying it.
12683
+ - Use \`code-intel query "TRAVERSE CALLS FROM '<symbol>' DEPTH 3"\` to trace execution paths.
12684
+
12685
+ ### Before Committing / Code Review
12686
+ - Run \`code-intel impact <symbol>\` for every symbol you changed.
12687
+ - Run \`code-intel pr-impact --base main --head HEAD\` to see full PR blast radius.
12688
+ - Fail PR if HIGH risk symbols are changed without reviewer sign-off.
12689
+
12690
+ ### Studying the Codebase
12691
+ - Use \`code-intel search "<concept>"\` to explore unfamiliar areas.
12692
+ - Use \`code-intel inspect <symbol>\` to see a symbol's full context.
12693
+ - Use \`code-intel serve\` to open the interactive Web UI for graph exploration.
12694
+ - Use subsystem skills (see table below) for deep-dive on a specific area.
12695
+
12696
+ ## Never Do
12697
+
12698
+ - NEVER ignore impact warnings \u2014 always report blast radius to the user.
12699
+ - NEVER skip \`code-intel search\` before grepping or opening files.
12700
+ - NEVER make changes to a symbol with \u2265 5 callers without running \`code-intel impact\` first.
12701
+ - NEVER use find-and-replace for symbol renames.
12702
+
12703
+ ## Development Workflow
12704
+
12705
+ ### \u{1F527} Implement a New Feature
12706
+ \`\`\`
12707
+ 1. code-intel search "<feature concept>" # find related existing symbols
12708
+ 2. code-intel inspect <related-symbol> # understand context & callers
12709
+ 3. Load subsystem skill (see Skills table) # deep-dive the area
12710
+ 4. Implement changes
12711
+ 5. code-intel impact <changed-symbol> # verify blast radius
12712
+ 6. code-intel pr-impact --base main # full PR summary before commit
12713
+ \`\`\`
12714
+
12715
+ ### \u{1F41B} Fix a Bug
12716
+ \`\`\`
12717
+ 1. code-intel search "<buggy behavior>" # locate the symbol
12718
+ 2. code-intel query "TRAVERSE CALLS FROM '<symbol>' DEPTH 3" # trace execution path
12719
+ 3. code-intel inspect <symbol> # find all callers that may be affected
12720
+ 4. Fix the bug
12721
+ 5. code-intel impact <symbol> # confirm no unexpected side effects
12722
+ \`\`\`
12723
+
12724
+ ### \u{1F52C} Study / Understand Code
12725
+ \`\`\`
12726
+ 1. code-intel search "<concept>" # discover entry points
12727
+ 2. code-intel inspect <symbol> # full context: callers, callees, imports
12728
+ 3. code-intel query "TRAVERSE CALLS FROM '<symbol>' DEPTH 3" # execution call graph
12729
+ 4. code-intel query "PATH FROM '<symbol>' TO '<target>'" # path between two symbols
12730
+ 5. Load subsystem skill # structured deep-dive
12731
+ \`\`\`
12732
+
12733
+ ### \u{1F440} Code Review
12734
+ \`\`\`
12735
+ 1. code-intel pr-impact --base main --head HEAD # blast radius of all PR changes
12736
+ 2. code-intel impact <each-changed-symbol> # per-symbol risk check
12737
+ 3. Flag HIGH risk (\u2265 5 callers) for reviewer sign-off
12738
+ \`\`\`
12739
+
12740
+ ### \u{1F504} Maintain / Refactor
12741
+ \`\`\`
12742
+ 1. code-intel inspect <symbol> # find ALL usages before touching
12743
+ 2. code-intel impact <symbol> # blast radius \u2014 plan your changes
12744
+ 3. Make changes incrementally
12745
+ 4. code-intel pr-impact --base main # validate scope hasn't exploded
12746
+ \`\`\`
11999
12747
 
12000
12748
  ## CLI Quick Reference
12001
12749
 
12002
12750
  \`\`\`bash
12003
- code-intel analyze [path] # Build / refresh the knowledge graph
12004
- code-intel serve [path] # Start HTTP API + Web UI on :4747
12005
- code-intel search <query> # Text search across all symbols
12006
- code-intel inspect <symbol> # Callers, callees, imports, cluster
12007
- code-intel impact <symbol> # Blast radius (who breaks if this changes)
12008
- code-intel status [path] # Index freshness and stats
12009
- code-intel clean [path] # Remove index data
12751
+ code-intel analyze [path] # Build / refresh the knowledge graph
12752
+ code-intel serve [path] # Start HTTP API + Web UI on :4747
12753
+ code-intel search <query> # Find symbols by concept/name
12754
+ code-intel inspect <symbol> # Callers, callees, imports, cluster
12755
+ code-intel impact <symbol> # Blast radius (who breaks if this changes)
12756
+ code-intel query "TRAVERSE CALLS FROM '<symbol>' DEPTH 3" # Trace execution call graph
12757
+ code-intel query "PATH FROM '<sym>' TO '<target>'" # Find path between two symbols
12758
+ code-intel query "FIND function WHERE name CONTAINS '<x>'" # GQL symbol search
12759
+ code-intel pr-impact --base main --head HEAD # Full PR blast radius report
12760
+ code-intel complexity [path] --top 10 # Cyclomatic complexity hotspots
12761
+ code-intel coverage [path] # Untested exported symbols by blast radius
12762
+ code-intel secrets [path] # Scan for hardcoded secrets
12763
+ code-intel scan [path] --severity high # OWASP vulnerability scan
12764
+ code-intel deprecated [path] # Find deprecated API usages
12765
+ code-intel status [path] # Index freshness and stats
12766
+ code-intel clean [path] # Remove index data
12010
12767
  \`\`\`
12011
12768
 
12012
12769
  ## Skills
12013
-
12770
+ ${skillLoadInstructions}
12014
12771
  ${skillTable}
12015
12772
  ${BLOCK_END}`;
12016
12773
  }
12017
12774
  function upsertFile(filePath, block, fileName) {
12018
- if (!fs33.existsSync(filePath)) {
12775
+ if (!fs34.existsSync(filePath)) {
12019
12776
  const newContent = [
12020
12777
  `# ${fileName}`,
12021
12778
  "",
@@ -12026,17 +12783,17 @@ function upsertFile(filePath, block, fileName) {
12026
12783
  "<!-- Add your own custom notes below this line. They will never be overwritten by code-intel. -->",
12027
12784
  ""
12028
12785
  ].join("\n");
12029
- fs33.writeFileSync(filePath, newContent, "utf-8");
12786
+ fs34.writeFileSync(filePath, newContent, "utf-8");
12030
12787
  return;
12031
12788
  }
12032
- const existing = fs33.readFileSync(filePath, "utf-8");
12789
+ const existing = fs34.readFileSync(filePath, "utf-8");
12033
12790
  const startIdx = findLineMarker(existing, BLOCK_START);
12034
12791
  const endIdx = findLineMarker(existing, BLOCK_END, startIdx === -1 ? 0 : startIdx);
12035
12792
  if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
12036
12793
  const before = existing.slice(0, startIdx);
12037
12794
  const after = existing.slice(endIdx + BLOCK_END.length);
12038
12795
  const updated = (before + block + after).trimEnd() + "\n";
12039
- fs33.writeFileSync(filePath, updated, "utf-8");
12796
+ fs34.writeFileSync(filePath, updated, "utf-8");
12040
12797
  return;
12041
12798
  }
12042
12799
  const appended = [
@@ -12049,7 +12806,7 @@ function upsertFile(filePath, block, fileName) {
12049
12806
  block,
12050
12807
  ""
12051
12808
  ].join("\n");
12052
- fs33.writeFileSync(filePath, appended, "utf-8");
12809
+ fs34.writeFileSync(filePath, appended, "utf-8");
12053
12810
  }
12054
12811
  function findLineMarker(content, marker, startFrom = 0) {
12055
12812
  let idx = content.indexOf(marker, startFrom);
@@ -12091,14 +12848,14 @@ function getChangedFilesSince(workspaceRoot, baseHash) {
12091
12848
  function filterChangedByMtime(allFilePaths, workspaceRoot, storedMtimes) {
12092
12849
  const changed = [];
12093
12850
  for (const absPath of allFilePaths) {
12094
- const rel = path33.relative(workspaceRoot, absPath);
12851
+ const rel = path35.relative(workspaceRoot, absPath);
12095
12852
  const stored = storedMtimes[rel];
12096
12853
  if (stored === void 0) {
12097
12854
  changed.push(absPath);
12098
12855
  continue;
12099
12856
  }
12100
12857
  try {
12101
- const { mtimeMs } = fs33.statSync(absPath);
12858
+ const { mtimeMs } = fs34.statSync(absPath);
12102
12859
  if (mtimeMs > stored) changed.push(absPath);
12103
12860
  } catch {
12104
12861
  changed.push(absPath);
@@ -12110,8 +12867,8 @@ function buildMtimeSnapshot(filePaths, workspaceRoot) {
12110
12867
  const snap = {};
12111
12868
  for (const absPath of filePaths) {
12112
12869
  try {
12113
- const { mtimeMs } = fs33.statSync(absPath);
12114
- snap[path33.relative(workspaceRoot, absPath)] = mtimeMs;
12870
+ const { mtimeMs } = fs34.statSync(absPath);
12871
+ snap[path35.relative(workspaceRoot, absPath)] = mtimeMs;
12115
12872
  } catch {
12116
12873
  }
12117
12874
  }
@@ -12122,8 +12879,8 @@ function decideIncremental(workspaceRoot, allFilePaths, prevCommitHash, storedMt
12122
12879
  if (prevCommitHash) {
12123
12880
  const changed = getChangedFilesSince(workspaceRoot, prevCommitHash);
12124
12881
  if (changed !== null) {
12125
- const scanSet = new Set(allFilePaths.map((p) => path33.relative(workspaceRoot, p)));
12126
- const changedInScan = changed.filter((rel) => scanSet.has(rel)).map((rel) => path33.join(workspaceRoot, rel));
12882
+ const scanSet = new Set(allFilePaths.map((p) => path35.relative(workspaceRoot, p)));
12883
+ const changedInScan = changed.filter((rel) => scanSet.has(rel)).map((rel) => path35.join(workspaceRoot, rel));
12127
12884
  if (total > 0 && changedInScan.length / total > 0.2) {
12128
12885
  return { incremental: false, fallbackReason: `changed files (${changedInScan.length}) > 20% of total (${total})` };
12129
12886
  }
@@ -12150,11 +12907,11 @@ init_group_sync();
12150
12907
  function expandGlob(root, pattern) {
12151
12908
  const parts = pattern.replace(/\/\*\*?$/, "").split("/").filter(Boolean);
12152
12909
  if (parts.length === 0) return [];
12153
- const dir = path33.join(root, ...parts);
12154
- if (!fs33.existsSync(dir)) return [];
12155
- return fs33.readdirSync(dir).map((entry) => path33.join(dir, entry)).filter((p) => {
12910
+ const dir = path35.join(root, ...parts);
12911
+ if (!fs34.existsSync(dir)) return [];
12912
+ return fs34.readdirSync(dir).map((entry) => path35.join(dir, entry)).filter((p) => {
12156
12913
  try {
12157
- return fs33.statSync(p).isDirectory();
12914
+ return fs34.statSync(p).isDirectory();
12158
12915
  } catch {
12159
12916
  return false;
12160
12917
  }
@@ -12165,11 +12922,11 @@ function resolvePackages(root, patterns) {
12165
12922
  for (const pattern of patterns) {
12166
12923
  const dirs = expandGlob(root, pattern);
12167
12924
  for (const dir of dirs) {
12168
- const pkgJsonPath = path33.join(dir, "package.json");
12169
- if (!fs33.existsSync(pkgJsonPath)) continue;
12925
+ const pkgJsonPath = path35.join(dir, "package.json");
12926
+ if (!fs34.existsSync(pkgJsonPath)) continue;
12170
12927
  try {
12171
- const pkgJson = JSON.parse(fs33.readFileSync(pkgJsonPath, "utf-8"));
12172
- const name = pkgJson.name ?? path33.basename(dir);
12928
+ const pkgJson = JSON.parse(fs34.readFileSync(pkgJsonPath, "utf-8"));
12929
+ const name = pkgJson.name ?? path35.basename(dir);
12173
12930
  packages.push({ name, path: dir });
12174
12931
  } catch {
12175
12932
  }
@@ -12178,13 +12935,13 @@ function resolvePackages(root, patterns) {
12178
12935
  return packages;
12179
12936
  }
12180
12937
  async function detectWorkspace(root) {
12181
- const turboJsonPath = path33.join(root, "turbo.json");
12182
- if (fs33.existsSync(turboJsonPath)) {
12938
+ const turboJsonPath = path35.join(root, "turbo.json");
12939
+ if (fs34.existsSync(turboJsonPath)) {
12183
12940
  let patterns = [];
12184
- const pkgJsonPath = path33.join(root, "package.json");
12185
- if (fs33.existsSync(pkgJsonPath)) {
12941
+ const pkgJsonPath = path35.join(root, "package.json");
12942
+ if (fs34.existsSync(pkgJsonPath)) {
12186
12943
  try {
12187
- const pkgJson = JSON.parse(fs33.readFileSync(pkgJsonPath, "utf-8"));
12944
+ const pkgJson = JSON.parse(fs34.readFileSync(pkgJsonPath, "utf-8"));
12188
12945
  if (pkgJson.workspaces) {
12189
12946
  patterns = Array.isArray(pkgJson.workspaces) ? pkgJson.workspaces : pkgJson.workspaces.packages;
12190
12947
  }
@@ -12192,16 +12949,16 @@ async function detectWorkspace(root) {
12192
12949
  }
12193
12950
  }
12194
12951
  if (patterns.length === 0) {
12195
- const fallbackDir = path33.join(root, "packages");
12196
- if (fs33.existsSync(fallbackDir)) patterns = ["packages/*"];
12952
+ const fallbackDir = path35.join(root, "packages");
12953
+ if (fs34.existsSync(fallbackDir)) patterns = ["packages/*"];
12197
12954
  }
12198
12955
  const packages = resolvePackages(root, patterns);
12199
12956
  return { type: "turborepo", root, packages };
12200
12957
  }
12201
- const npmPkgJsonPath = path33.join(root, "package.json");
12202
- if (fs33.existsSync(npmPkgJsonPath)) {
12958
+ const npmPkgJsonPath = path35.join(root, "package.json");
12959
+ if (fs34.existsSync(npmPkgJsonPath)) {
12203
12960
  try {
12204
- const pkgJson = JSON.parse(fs33.readFileSync(npmPkgJsonPath, "utf-8"));
12961
+ const pkgJson = JSON.parse(fs34.readFileSync(npmPkgJsonPath, "utf-8"));
12205
12962
  if (pkgJson.workspaces) {
12206
12963
  const patterns = Array.isArray(pkgJson.workspaces) ? pkgJson.workspaces : pkgJson.workspaces.packages;
12207
12964
  const packages = resolvePackages(root, patterns);
@@ -12210,11 +12967,11 @@ async function detectWorkspace(root) {
12210
12967
  } catch {
12211
12968
  }
12212
12969
  }
12213
- const pnpmYamlPath = path33.join(root, "pnpm-workspace.yaml");
12214
- if (fs33.existsSync(pnpmYamlPath)) {
12970
+ const pnpmYamlPath = path35.join(root, "pnpm-workspace.yaml");
12971
+ if (fs34.existsSync(pnpmYamlPath)) {
12215
12972
  const patterns = [];
12216
12973
  try {
12217
- const content = fs33.readFileSync(pnpmYamlPath, "utf-8");
12974
+ const content = fs34.readFileSync(pnpmYamlPath, "utf-8");
12218
12975
  let inPackages = false;
12219
12976
  for (const line of content.split("\n")) {
12220
12977
  if (/^packages\s*:/.test(line)) {
@@ -12234,30 +12991,30 @@ async function detectWorkspace(root) {
12234
12991
  const packages = resolvePackages(root, patterns);
12235
12992
  return { type: "pnpm", root, packages };
12236
12993
  }
12237
- const nxJsonPath = path33.join(root, "nx.json");
12238
- if (fs33.existsSync(nxJsonPath)) {
12994
+ const nxJsonPath = path35.join(root, "nx.json");
12995
+ if (fs34.existsSync(nxJsonPath)) {
12239
12996
  const packages = [];
12240
12997
  const scanForProjects = (dir, depth) => {
12241
12998
  if (depth > 2) return;
12242
12999
  let entries;
12243
13000
  try {
12244
- entries = fs33.readdirSync(dir);
13001
+ entries = fs34.readdirSync(dir);
12245
13002
  } catch {
12246
13003
  return;
12247
13004
  }
12248
13005
  for (const entry of entries) {
12249
13006
  if (entry === "node_modules" || entry.startsWith(".")) continue;
12250
- const fullPath = path33.join(dir, entry);
13007
+ const fullPath = path35.join(dir, entry);
12251
13008
  try {
12252
- if (!fs33.statSync(fullPath).isDirectory()) continue;
13009
+ if (!fs34.statSync(fullPath).isDirectory()) continue;
12253
13010
  } catch {
12254
13011
  continue;
12255
13012
  }
12256
- const projectJsonPath = path33.join(fullPath, "project.json");
12257
- if (fs33.existsSync(projectJsonPath)) {
13013
+ const projectJsonPath = path35.join(fullPath, "project.json");
13014
+ if (fs34.existsSync(projectJsonPath)) {
12258
13015
  try {
12259
- const proj = JSON.parse(fs33.readFileSync(projectJsonPath, "utf-8"));
12260
- const name = proj.name ?? path33.basename(fullPath);
13016
+ const proj = JSON.parse(fs34.readFileSync(projectJsonPath, "utf-8"));
13017
+ const name = proj.name ?? path35.basename(fullPath);
12261
13018
  packages.push({ name, path: fullPath });
12262
13019
  } catch {
12263
13020
  }
@@ -12365,17 +13122,17 @@ var MigrationRunner = class {
12365
13122
  autoBackupBeforeMigration() {
12366
13123
  try {
12367
13124
  const dbFile = this.db.name;
12368
- if (!dbFile || !fs33.existsSync(dbFile)) return;
12369
- const backupDir = path33.join(os12.homedir(), ".code-intel", "backups", "pre-migration");
12370
- fs33.mkdirSync(backupDir, { recursive: true });
13125
+ if (!dbFile || !fs34.existsSync(dbFile)) return;
13126
+ const backupDir = path35.join(os12.homedir(), ".code-intel", "backups", "pre-migration");
13127
+ fs34.mkdirSync(backupDir, { recursive: true });
12371
13128
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
12372
- const baseName = path33.basename(dbFile, ".db");
12373
- const backupPath = path33.join(backupDir, `${baseName}-pre-migration-${ts}.db`);
13129
+ const baseName = path35.basename(dbFile, ".db");
13130
+ const backupPath = path35.join(backupDir, `${baseName}-pre-migration-${ts}.db`);
12374
13131
  try {
12375
13132
  this.db.pragma("wal_checkpoint(TRUNCATE)");
12376
13133
  } catch {
12377
13134
  }
12378
- fs33.copyFileSync(dbFile, backupPath);
13135
+ fs34.copyFileSync(dbFile, backupPath);
12379
13136
  } catch {
12380
13137
  }
12381
13138
  }
@@ -12591,7 +13348,7 @@ program.name("code-intel").description("Code Intelligence Platform \u2014 Static
12591
13348
  Docs: https://github.com/vohongtho/code-intel-platform
12592
13349
  `);
12593
13350
  async function analyzeWorkspace(targetPath, options) {
12594
- const workspaceRoot = path33.resolve(targetPath);
13351
+ const workspaceRoot = path35.resolve(targetPath);
12595
13352
  if (!options?.silent) console.log(`Analyzing: ${workspaceRoot}`);
12596
13353
  logger_default.info(`analyze started: ${workspaceRoot}`);
12597
13354
  if (options?.force) {
@@ -12612,14 +13369,14 @@ async function analyzeWorkspace(targetPath, options) {
12612
13369
  ];
12613
13370
  for (const f of wipeFiles) {
12614
13371
  try {
12615
- if (fs33.existsSync(f)) fs33.unlinkSync(f);
13372
+ if (fs34.existsSync(f)) fs34.unlinkSync(f);
12616
13373
  } catch {
12617
13374
  }
12618
13375
  }
12619
13376
  }
12620
13377
  if (!options?.skipGit) {
12621
- const gitDir = path33.join(workspaceRoot, ".git");
12622
- if (!fs33.existsSync(gitDir)) {
13378
+ const gitDir = path35.join(workspaceRoot, ".git");
13379
+ if (!fs34.existsSync(gitDir)) {
12623
13380
  logger_default.warn(`${workspaceRoot} is not a Git repository`);
12624
13381
  }
12625
13382
  }
@@ -12730,17 +13487,17 @@ async function analyzeWorkspace(targetPath, options) {
12730
13487
  const result = await runPipeline(phases, context2);
12731
13488
  if (isIncremental && incrementalChangedFiles && incrementalChangedFiles.length > 0) {
12732
13489
  const dbPath = getDbPath(workspaceRoot);
12733
- if (fs33.existsSync(dbPath)) {
13490
+ if (fs34.existsSync(dbPath)) {
12734
13491
  try {
12735
13492
  const db = new DbManager(dbPath);
12736
13493
  await db.init();
12737
13494
  for (const absPath of incrementalChangedFiles) {
12738
- const rel = path33.relative(workspaceRoot, absPath);
13495
+ const rel = path35.relative(workspaceRoot, absPath);
12739
13496
  await removeNodesForFile(rel, db);
12740
13497
  }
12741
13498
  const { upsertNodes: upsertNodesBatch } = await Promise.resolve().then(() => (init_graph_loader(), graph_loader_exports));
12742
13499
  const changedRelPaths = new Set(
12743
- incrementalChangedFiles.map((f) => path33.relative(workspaceRoot, f))
13500
+ incrementalChangedFiles.map((f) => path35.relative(workspaceRoot, f))
12744
13501
  );
12745
13502
  const nodesToUpsert = [...graph.allNodes()].filter(
12746
13503
  (n) => changedRelPaths.has(n.filePath)
@@ -12765,7 +13522,7 @@ async function analyzeWorkspace(targetPath, options) {
12765
13522
  mergedMtimes = newMtimes;
12766
13523
  }
12767
13524
  const currentCommitHash = getCurrentCommitHash(workspaceRoot) ?? void 0;
12768
- const repoName = path33.basename(workspaceRoot);
13525
+ const repoName = path35.basename(workspaceRoot);
12769
13526
  const indexVersion = v4();
12770
13527
  saveMetadata(workspaceRoot, {
12771
13528
  indexedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -12803,7 +13560,7 @@ async function analyzeWorkspace(targetPath, options) {
12803
13560
  ];
12804
13561
  for (const f of newStaleFiles) {
12805
13562
  try {
12806
- if (fs33.existsSync(f)) fs33.unlinkSync(f);
13563
+ if (fs34.existsSync(f)) fs34.unlinkSync(f);
12807
13564
  } catch {
12808
13565
  }
12809
13566
  }
@@ -12820,21 +13577,21 @@ async function analyzeWorkspace(targetPath, options) {
12820
13577
  ];
12821
13578
  for (const f of staleFiles) {
12822
13579
  try {
12823
- if (fs33.existsSync(f)) fs33.unlinkSync(f);
13580
+ if (fs34.existsSync(f)) fs34.unlinkSync(f);
12824
13581
  } catch {
12825
13582
  }
12826
13583
  }
12827
13584
  for (const f of newStaleFiles) {
12828
13585
  if (f === dbPathNew) continue;
12829
- if (fs33.existsSync(f)) {
13586
+ if (fs34.existsSync(f)) {
12830
13587
  const dest = f.replace(dbPathNew, dbPath);
12831
13588
  try {
12832
- fs33.renameSync(f, dest);
13589
+ fs34.renameSync(f, dest);
12833
13590
  } catch {
12834
13591
  }
12835
13592
  }
12836
13593
  }
12837
- fs33.renameSync(dbPathNew, dbPath);
13594
+ fs34.renameSync(dbPathNew, dbPath);
12838
13595
  stopSpinner();
12839
13596
  logger_default.info(`DB persisted: ${nodeCount} nodes, ${edgeCount} edges`);
12840
13597
  if (!options?.silent) {
@@ -12855,7 +13612,7 @@ async function analyzeWorkspace(targetPath, options) {
12855
13612
  const staleVdb = [vdbPath, `${vdbPath}-shm`, `${vdbPath}-wal`];
12856
13613
  for (const f of staleVdb) {
12857
13614
  try {
12858
- if (fs33.existsSync(f)) fs33.unlinkSync(f);
13615
+ if (fs34.existsSync(f)) fs34.unlinkSync(f);
12859
13616
  } catch {
12860
13617
  }
12861
13618
  }
@@ -12979,8 +13736,8 @@ program.command("setup").description("Configure MCP server for your editors (one
12979
13736
  const configFile = `${configDir}/claude_desktop_config.json`;
12980
13737
  try {
12981
13738
  let existing = {};
12982
- if (fs33.existsSync(configFile)) {
12983
- existing = JSON.parse(fs33.readFileSync(configFile, "utf-8"));
13739
+ if (fs34.existsSync(configFile)) {
13740
+ existing = JSON.parse(fs34.readFileSync(configFile, "utf-8"));
12984
13741
  }
12985
13742
  const merged = {
12986
13743
  ...existing,
@@ -12989,8 +13746,8 @@ program.command("setup").description("Configure MCP server for your editors (one
12989
13746
  ...mcpConfig.mcpServers
12990
13747
  }
12991
13748
  };
12992
- fs33.mkdirSync(configDir, { recursive: true });
12993
- fs33.writeFileSync(configFile, JSON.stringify(merged, null, 2) + "\n", "utf-8");
13749
+ fs34.mkdirSync(configDir, { recursive: true });
13750
+ fs34.writeFileSync(configFile, JSON.stringify(merged, null, 2) + "\n", "utf-8");
12994
13751
  console.log(`
12995
13752
  \u2705 Written to ${configFile}`);
12996
13753
  } catch (err) {
@@ -13060,10 +13817,10 @@ program.command("mcp").description("Start MCP server over stdio \u2014 exposes a
13060
13817
  $ code-intel mcp
13061
13818
  $ code-intel mcp ./my-project
13062
13819
  `).action(async (targetPath) => {
13063
- const workspaceRoot = path33.resolve(targetPath);
13064
- const repoName = path33.basename(workspaceRoot);
13820
+ const workspaceRoot = path35.resolve(targetPath);
13821
+ const repoName = path35.basename(workspaceRoot);
13065
13822
  const dbPath = getDbPath(workspaceRoot);
13066
- const existingIndex = fs33.existsSync(dbPath) && loadMetadata(workspaceRoot) !== null;
13823
+ const existingIndex = fs34.existsSync(dbPath) && loadMetadata(workspaceRoot) !== null;
13067
13824
  if (existingIndex) {
13068
13825
  const graph = createKnowledgeGraph();
13069
13826
  const db = new DbManager(dbPath);
@@ -13094,10 +13851,10 @@ program.command("serve").description("Start the local HTTP server + web UI for g
13094
13851
  $ code-intel serve --port 8080
13095
13852
  $ code-intel serve --force
13096
13853
  `).action(async (targetPath, options) => {
13097
- const workspaceRoot = path33.resolve(targetPath);
13098
- const repoName = path33.basename(workspaceRoot);
13854
+ const workspaceRoot = path35.resolve(targetPath);
13855
+ const repoName = path35.basename(workspaceRoot);
13099
13856
  const dbPath = getDbPath(workspaceRoot);
13100
- const existingIndex = !options.force && fs33.existsSync(dbPath) && loadMetadata(workspaceRoot) !== null;
13857
+ const existingIndex = !options.force && fs34.existsSync(dbPath) && loadMetadata(workspaceRoot) !== null;
13101
13858
  if (existingIndex) {
13102
13859
  const meta = loadMetadata(workspaceRoot);
13103
13860
  if (meta.parser === "regex" || meta.parser === void 0) {
@@ -13131,10 +13888,10 @@ program.command("watch").description("Start HTTP server + file watcher (auto-rei
13131
13888
  `).action(async (targetPath, options) => {
13132
13889
  const { FileWatcher: FileWatcher2 } = await Promise.resolve().then(() => (init_file_watcher(), file_watcher_exports));
13133
13890
  const { IncrementalIndexer: IncrementalIndexer2 } = await Promise.resolve().then(() => (init_incremental_indexer(), incremental_indexer_exports));
13134
- const workspaceRoot = path33.resolve(targetPath);
13135
- const repoName = path33.basename(workspaceRoot);
13891
+ const workspaceRoot = path35.resolve(targetPath);
13892
+ const repoName = path35.basename(workspaceRoot);
13136
13893
  const dbPath = getDbPath(workspaceRoot);
13137
- const existingIndex = !options.force && fs33.existsSync(dbPath) && loadMetadata(workspaceRoot) !== null;
13894
+ const existingIndex = !options.force && fs34.existsSync(dbPath) && loadMetadata(workspaceRoot) !== null;
13138
13895
  let graph;
13139
13896
  if (existingIndex) {
13140
13897
  const meta = loadMetadata(workspaceRoot);
@@ -13169,7 +13926,7 @@ program.command("watch").description("Start HTTP server + file watcher (auto-rei
13169
13926
  type: "graph:updated",
13170
13927
  indexVersion: meta?.indexVersion ?? "unknown",
13171
13928
  stats: { nodes: graph.size.nodes, edges: graph.size.edges },
13172
- changedFiles: changedFiles.map((f) => path33.relative(workspaceRoot, f)),
13929
+ changedFiles: changedFiles.map((f) => path35.relative(workspaceRoot, f)),
13173
13930
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
13174
13931
  });
13175
13932
  }
@@ -13220,7 +13977,7 @@ program.command("status").description("Show index freshness and statistics for a
13220
13977
  $ code-intel status
13221
13978
  $ code-intel status ./my-project
13222
13979
  `).action((targetPath) => {
13223
- const workspaceRoot = path33.resolve(targetPath);
13980
+ const workspaceRoot = path35.resolve(targetPath);
13224
13981
  const meta = loadMetadata(workspaceRoot);
13225
13982
  if (!meta) {
13226
13983
  console.log(`
@@ -13244,18 +14001,18 @@ function trashDirName(repoPath) {
13244
14001
  return `.code-intel-trash-${date}`;
13245
14002
  }
13246
14003
  function softDeleteCodeIntel(repoPath) {
13247
- const codeIntelDir = path33.join(repoPath, ".code-intel");
13248
- if (!fs33.existsSync(codeIntelDir)) return;
14004
+ const codeIntelDir = path35.join(repoPath, ".code-intel");
14005
+ if (!fs34.existsSync(codeIntelDir)) return;
13249
14006
  const trashName = trashDirName();
13250
- const trashDir = path33.join(repoPath, trashName);
14007
+ const trashDir = path35.join(repoPath, trashName);
13251
14008
  let dest = trashDir;
13252
14009
  let counter = 1;
13253
- while (fs33.existsSync(dest)) {
14010
+ while (fs34.existsSync(dest)) {
13254
14011
  dest = `${trashDir}-${counter++}`;
13255
14012
  }
13256
- fs33.renameSync(codeIntelDir, dest);
13257
- fs33.writeFileSync(
13258
- path33.join(dest, "TRASH_META.json"),
14013
+ fs34.renameSync(codeIntelDir, dest);
14014
+ fs34.writeFileSync(
14015
+ path35.join(dest, "TRASH_META.json"),
13259
14016
  JSON.stringify({ deletedAt: (/* @__PURE__ */ new Date()).toISOString(), repoPath, permanent: false }, null, 2)
13260
14017
  );
13261
14018
  console.log(` \u2713 Moved to trash: ${dest}`);
@@ -13264,15 +14021,15 @@ function softDeleteCodeIntel(repoPath) {
13264
14021
  function purgeStaleTrashes(repoPath) {
13265
14022
  const cutoff = Date.now() - TRASH_TTL_DAYS * 24 * 60 * 60 * 1e3;
13266
14023
  try {
13267
- for (const entry of fs33.readdirSync(repoPath)) {
14024
+ for (const entry of fs34.readdirSync(repoPath)) {
13268
14025
  if (!entry.startsWith(".code-intel-trash-")) continue;
13269
- const fullPath = path33.join(repoPath, entry);
13270
- const metaPath = path33.join(fullPath, "TRASH_META.json");
13271
- if (fs33.existsSync(metaPath)) {
14026
+ const fullPath = path35.join(repoPath, entry);
14027
+ const metaPath = path35.join(fullPath, "TRASH_META.json");
14028
+ if (fs34.existsSync(metaPath)) {
13272
14029
  try {
13273
- const meta = JSON.parse(fs33.readFileSync(metaPath, "utf-8"));
14030
+ const meta = JSON.parse(fs34.readFileSync(metaPath, "utf-8"));
13274
14031
  if (new Date(meta.deletedAt).getTime() < cutoff) {
13275
- fs33.rmSync(fullPath, { recursive: true, force: true });
14032
+ fs34.rmSync(fullPath, { recursive: true, force: true });
13276
14033
  console.log(` \u2713 Auto-purged stale trash: ${fullPath}`);
13277
14034
  }
13278
14035
  } catch {
@@ -13299,18 +14056,18 @@ program.command("clean").description("Soft-delete the knowledge graph index for
13299
14056
  if (opts.listTrash) {
13300
14057
  const repos = loadRegistry();
13301
14058
  const roots = repos.map((r) => r.path);
13302
- if (roots.length === 0) roots.push(path33.resolve("."));
14059
+ if (roots.length === 0) roots.push(path35.resolve("."));
13303
14060
  let found = 0;
13304
14061
  for (const root of roots) {
13305
14062
  try {
13306
- for (const entry of fs33.readdirSync(root)) {
14063
+ for (const entry of fs34.readdirSync(root)) {
13307
14064
  if (!entry.startsWith(".code-intel-trash-")) continue;
13308
- const fullPath = path33.join(root, entry);
13309
- const metaPath = path33.join(fullPath, "TRASH_META.json");
14065
+ const fullPath = path35.join(root, entry);
14066
+ const metaPath = path35.join(fullPath, "TRASH_META.json");
13310
14067
  let deletedAt = "unknown";
13311
- if (fs33.existsSync(metaPath)) {
14068
+ if (fs34.existsSync(metaPath)) {
13312
14069
  try {
13313
- deletedAt = JSON.parse(fs33.readFileSync(metaPath, "utf-8")).deletedAt;
14070
+ deletedAt = JSON.parse(fs34.readFileSync(metaPath, "utf-8")).deletedAt;
13314
14071
  } catch {
13315
14072
  }
13316
14073
  }
@@ -13338,9 +14095,9 @@ program.command("clean").description("Soft-delete the knowledge graph index for
13338
14095
  }
13339
14096
  for (const r of repos) {
13340
14097
  if (opts.purge) {
13341
- const codeIntelDir = path33.join(r.path, ".code-intel");
13342
- if (fs33.existsSync(codeIntelDir)) {
13343
- fs33.rmSync(codeIntelDir, { recursive: true, force: true });
14098
+ const codeIntelDir = path35.join(r.path, ".code-intel");
14099
+ if (fs34.existsSync(codeIntelDir)) {
14100
+ fs34.rmSync(codeIntelDir, { recursive: true, force: true });
13344
14101
  console.log(` \u2713 Hard-deleted ${codeIntelDir}`);
13345
14102
  }
13346
14103
  } else {
@@ -13354,11 +14111,11 @@ program.command("clean").description("Soft-delete the knowledge graph index for
13354
14111
  `);
13355
14112
  return;
13356
14113
  }
13357
- const workspaceRoot = path33.resolve(targetPath);
14114
+ const workspaceRoot = path35.resolve(targetPath);
13358
14115
  if (opts.purge) {
13359
- const codeIntelDir = path33.join(workspaceRoot, ".code-intel");
13360
- if (fs33.existsSync(codeIntelDir)) {
13361
- fs33.rmSync(codeIntelDir, { recursive: true, force: true });
14116
+ const codeIntelDir = path35.join(workspaceRoot, ".code-intel");
14117
+ if (fs34.existsSync(codeIntelDir)) {
14118
+ fs34.rmSync(codeIntelDir, { recursive: true, force: true });
13362
14119
  console.log(`
13363
14120
  \u2713 Hard-deleted ${codeIntelDir}`);
13364
14121
  }
@@ -13370,16 +14127,16 @@ program.command("clean").description("Soft-delete the knowledge graph index for
13370
14127
  console.log(" Index cleaned.\n");
13371
14128
  });
13372
14129
  async function loadOrAnalyzeWorkspace(targetPath) {
13373
- const workspaceRoot = path33.resolve(targetPath);
14130
+ const workspaceRoot = path35.resolve(targetPath);
13374
14131
  const dbPath = getDbPath(workspaceRoot);
13375
- const existingIndex = fs33.existsSync(dbPath) && loadMetadata(workspaceRoot) !== null;
14132
+ const existingIndex = fs34.existsSync(dbPath) && loadMetadata(workspaceRoot) !== null;
13376
14133
  if (existingIndex) {
13377
14134
  const graph = createKnowledgeGraph();
13378
14135
  const db = new DbManager(dbPath);
13379
14136
  await db.init();
13380
14137
  await loadGraphFromDB(graph, db);
13381
14138
  db.close();
13382
- return { graph, workspaceRoot, repoName: path33.basename(workspaceRoot) };
14139
+ return { graph, workspaceRoot, repoName: path35.basename(workspaceRoot) };
13383
14140
  }
13384
14141
  return analyzeWorkspace(targetPath, { silent: true });
13385
14142
  }
@@ -13514,6 +14271,43 @@ program.command("impact").description("Show the blast radius \u2014 all symbols
13514
14271
  }
13515
14272
  console.log("");
13516
14273
  });
14274
+ program.command("deprecated").description("Find usages of deprecated APIs in the codebase").argument("[path]", "Path to the repository (default: current directory)", ".").option("--format <fmt>", "Output format: table or json", "table").addHelpText("after", `
14275
+ Scans the knowledge graph for deprecated symbols (via @deprecated JSDoc,
14276
+ @Deprecated annotations, #[deprecated] attributes, or known Node.js APIs)
14277
+ and reports every caller.
14278
+
14279
+ Examples:
14280
+ $ code-intel deprecated
14281
+ $ code-intel deprecated ./src --format json
14282
+ `).action(async (targetPath, options) => {
14283
+ const { graph } = await loadOrAnalyzeWorkspace(targetPath);
14284
+ const { DeprecatedDetector: DeprecatedDetector2 } = await Promise.resolve().then(() => (init_deprecated_detector(), deprecated_detector_exports));
14285
+ const detector = new DeprecatedDetector2();
14286
+ detector.tagDeprecated(graph);
14287
+ const findings = detector.detect(graph);
14288
+ if (options.format === "json") {
14289
+ console.log(JSON.stringify({ findings, total: findings.length }, null, 2));
14290
+ return;
14291
+ }
14292
+ console.log("\n \u25C8 Deprecated API Usage Report\n");
14293
+ if (findings.length === 0) {
14294
+ console.log(" No deprecated API usages found.\n");
14295
+ return;
14296
+ }
14297
+ console.log(` ${"Symbol".padEnd(30)} ${"File".padEnd(40)} Message`);
14298
+ console.log(` ${"-".repeat(100)}`);
14299
+ for (const f of findings) {
14300
+ console.log(` ${f.symbol.padEnd(30)} ${f.filePath.padEnd(40)} ${f.deprecationMessage}`);
14301
+ if (f.callers.length > 0) {
14302
+ console.log(`
14303
+ Callers:`);
14304
+ for (const c of f.callers) {
14305
+ console.log(` ${c.name.padEnd(30)} ${c.filePath}`);
14306
+ }
14307
+ console.log("");
14308
+ }
14309
+ }
14310
+ });
13517
14311
  var groupCmd = program.command("group").description("Manage repository groups for multi-repo / monorepo service tracking").addHelpText("after", `
13518
14312
  Repository groups let you track contracts (exports, routes, schemas, events)
13519
14313
  across multiple indexed repos and detect cross-repo dependencies automatically.
@@ -13807,9 +14601,9 @@ groupCmd.command("status <name>").description("Check index freshness and sync st
13807
14601
  console.log(` \u2717 ${m.groupPath.padEnd(35)} [${m.registryName}] \u2014 NOT IN REGISTRY`);
13808
14602
  continue;
13809
14603
  }
13810
- const metaPath = path33.join(regEntry.path, ".code-intel", "meta.json");
14604
+ const metaPath = path35.join(regEntry.path, ".code-intel", "meta.json");
13811
14605
  try {
13812
- const meta = JSON.parse(fs33.readFileSync(metaPath, "utf-8"));
14606
+ const meta = JSON.parse(fs34.readFileSync(metaPath, "utf-8"));
13813
14607
  const indexedAt = meta.indexedAt;
13814
14608
  const ageMin = Math.round((now - new Date(indexedAt).getTime()) / 6e4);
13815
14609
  const stale = ageMin > 1440 ? " \u26A0 STALE (>24h)" : "";
@@ -13826,7 +14620,7 @@ groupCmd.command("status <name>").description("Check index freshness and sync st
13826
14620
  }
13827
14621
  });
13828
14622
  groupCmd.command("init-workspace [path]").description("Auto-discover workspace packages and create a group").option("--name <name>", "Group name (default: workspace root dirname)").option("--no-analyze", "Register packages without running analysis").option("--yes", "Skip confirmation prompt").option("--parallel <n>", "Concurrent analyses (default: 2)", "2").action(async (targetPath, opts) => {
13829
- const root = path33.resolve(targetPath ?? ".");
14623
+ const root = path35.resolve(targetPath ?? ".");
13830
14624
  const ws = await detectWorkspace(root);
13831
14625
  if (!ws) {
13832
14626
  console.error(`
@@ -13835,7 +14629,7 @@ groupCmd.command("init-workspace [path]").description("Auto-discover workspace p
13835
14629
  `);
13836
14630
  process.exit(1);
13837
14631
  }
13838
- const groupName = opts.name ?? path33.basename(root);
14632
+ const groupName = opts.name ?? path35.basename(root);
13839
14633
  console.log(`
13840
14634
  \u25C8 Workspace detected: ${ws.type}`);
13841
14635
  console.log(` Group name : ${groupName}`);
@@ -14114,7 +14908,7 @@ backupCmd.command("create [path]").description("Create an encrypted backup of th
14114
14908
  $ code-intel backup create
14115
14909
  $ code-intel backup create ./my-project
14116
14910
  `).action((targetPath = ".") => {
14117
- const repoPath = path33.resolve(targetPath);
14911
+ const repoPath = path35.resolve(targetPath);
14118
14912
  const svc = new BackupService();
14119
14913
  try {
14120
14914
  const entry = svc.createBackup(repoPath);
@@ -14143,7 +14937,7 @@ backupCmd.command("list").description("List all available backups").action(() =>
14143
14937
  Backups (${entries.length}):
14144
14938
  `);
14145
14939
  for (const e of entries) {
14146
- const exists = fs33.existsSync(e.path);
14940
+ const exists = fs34.existsSync(e.path);
14147
14941
  const status = exists ? "\u2713" : "\u2717 (missing)";
14148
14942
  console.log(` ${status} ${e.id.slice(0, 8)} ${e.createdAt} ${(e.size / 1024).toFixed(1)} KB \u2192 ${e.repoPath}`);
14149
14943
  }
@@ -14156,7 +14950,7 @@ backupCmd.command("restore <id>").description("Restore a backup by ID").option("
14156
14950
  `).action((id, opts) => {
14157
14951
  const svc = new BackupService();
14158
14952
  try {
14159
- const targetPath = opts.target ? path33.resolve(opts.target) : void 0;
14953
+ const targetPath = opts.target ? path35.resolve(opts.target) : void 0;
14160
14954
  svc.restoreBackup(id, targetPath);
14161
14955
  console.log(`
14162
14956
  \u2705 Backup "${id}" restored successfully.
@@ -14175,8 +14969,8 @@ program.command("migrate").description("Manage database schema migrations").opti
14175
14969
  $ code-intel migrate
14176
14970
  $ code-intel migrate --rollback
14177
14971
  `).action((opts) => {
14178
- const dbPath = opts.db ?? path33.join(os12.homedir(), ".code-intel", "users.db");
14179
- if (!fs33.existsSync(dbPath)) {
14972
+ const dbPath = opts.db ?? path35.join(os12.homedir(), ".code-intel", "users.db");
14973
+ if (!fs34.existsSync(dbPath)) {
14180
14974
  console.error(`
14181
14975
  \u2717 Database not found: ${dbPath}
14182
14976
  Run \`code-intel serve\` or \`code-intel user create\` first.
@@ -14291,15 +15085,15 @@ authCmd.command("login").description("Authenticate via OIDC device flow \u2014 o
14291
15085
  }
14292
15086
  try {
14293
15087
  const tokens = await pollDeviceFlow3(config, deviceResponse);
14294
- const tokenPath = path33.join(os12.homedir(), ".code-intel", "oidc-token.json");
15088
+ const tokenPath = path35.join(os12.homedir(), ".code-intel", "oidc-token.json");
14295
15089
  const tokenData = {
14296
15090
  accessToken: tokens.accessToken,
14297
15091
  refreshToken: tokens.refreshToken,
14298
15092
  server: serverUrl,
14299
15093
  storedAt: (/* @__PURE__ */ new Date()).toISOString()
14300
15094
  };
14301
- fs33.mkdirSync(path33.dirname(tokenPath), { recursive: true });
14302
- fs33.writeFileSync(tokenPath, JSON.stringify(tokenData, null, 2), { mode: 384 });
15095
+ fs34.mkdirSync(path35.dirname(tokenPath), { recursive: true });
15096
+ fs34.writeFileSync(tokenPath, JSON.stringify(tokenData, null, 2), { mode: 384 });
14303
15097
  console.log(` \u2705 Authenticated successfully!`);
14304
15098
  console.log(` Token stored at: ${tokenPath}`);
14305
15099
  console.log(` Use CODE_INTEL_TOKEN env var or --token flag to use it with CLI/MCP.
@@ -14312,13 +15106,13 @@ authCmd.command("login").description("Authenticate via OIDC device flow \u2014 o
14312
15106
  }
14313
15107
  });
14314
15108
  authCmd.command("status").description("Show the current OIDC authentication status").action(() => {
14315
- const tokenPath = path33.join(os12.homedir(), ".code-intel", "oidc-token.json");
14316
- if (!fs33.existsSync(tokenPath)) {
15109
+ const tokenPath = path35.join(os12.homedir(), ".code-intel", "oidc-token.json");
15110
+ if (!fs34.existsSync(tokenPath)) {
14317
15111
  console.log("\n Not authenticated via OIDC. Run: code-intel auth login\n");
14318
15112
  return;
14319
15113
  }
14320
15114
  try {
14321
- const data = JSON.parse(fs33.readFileSync(tokenPath, "utf-8"));
15115
+ const data = JSON.parse(fs34.readFileSync(tokenPath, "utf-8"));
14322
15116
  console.log(`
14323
15117
  \u2705 OIDC token stored`);
14324
15118
  console.log(` Server : ${data.server ?? "unknown"}`);
@@ -14330,9 +15124,9 @@ authCmd.command("status").description("Show the current OIDC authentication stat
14330
15124
  }
14331
15125
  });
14332
15126
  authCmd.command("logout").description("Remove locally stored OIDC token").action(() => {
14333
- const tokenPath = path33.join(os12.homedir(), ".code-intel", "oidc-token.json");
14334
- if (fs33.existsSync(tokenPath)) {
14335
- fs33.unlinkSync(tokenPath);
15127
+ const tokenPath = path35.join(os12.homedir(), ".code-intel", "oidc-token.json");
15128
+ if (fs34.existsSync(tokenPath)) {
15129
+ fs34.unlinkSync(tokenPath);
14336
15130
  console.log("\n \u2705 OIDC token removed. You are now logged out.\n");
14337
15131
  } else {
14338
15132
  console.log("\n No stored token found.\n");
@@ -14377,7 +15171,7 @@ authCmd.command("rotate-token <id>").description("Rotate an API token \u2014 iss
14377
15171
  console.log("\n Save the new token now \u2014 it will NOT be shown again!\n");
14378
15172
  console.log(" Usage: Authorization: Bearer <new-token>\n");
14379
15173
  });
14380
- var secretsCmd = program.command("secrets").description("Manage the encrypted .code-intel/.secrets store (AES-256-GCM)");
15174
+ var secretsCmd = program.command("keystore").description("Manage the encrypted .code-intel/.secrets store (AES-256-GCM)");
14381
15175
  secretsCmd.command("set <key> <value>").description("Store a secret by key").action((key, value) => {
14382
15176
  try {
14383
15177
  setSecret(key, value);
@@ -14456,8 +15250,8 @@ program.command("config-validate <file>").description("Validate a JSON config fi
14456
15250
  $ code-intel config-validate ./config.json
14457
15251
  $ code-intel config-validate ~/.code-intel/config.json
14458
15252
  `).action((file) => {
14459
- const filePath = path33.resolve(file);
14460
- if (!fs33.existsSync(filePath)) {
15253
+ const filePath = path35.resolve(file);
15254
+ if (!fs34.existsSync(filePath)) {
14461
15255
  console.error(`
14462
15256
  \u2717 File not found: ${filePath}
14463
15257
  `);
@@ -14465,7 +15259,7 @@ program.command("config-validate <file>").description("Validate a JSON config fi
14465
15259
  }
14466
15260
  let cfg;
14467
15261
  try {
14468
- cfg = JSON.parse(fs33.readFileSync(filePath, "utf-8"));
15262
+ cfg = JSON.parse(fs34.readFileSync(filePath, "utf-8"));
14469
15263
  } catch (err) {
14470
15264
  console.error(`
14471
15265
  \u2717 Could not parse JSON: ${err instanceof Error ? err.message : err}
@@ -14486,7 +15280,7 @@ ${err instanceof Error ? err.message : err}
14486
15280
  });
14487
15281
  (function ensurePermissions() {
14488
15282
  try {
14489
- const dir = path33.join(os12.homedir(), ".code-intel");
15283
+ const dir = path35.join(os12.homedir(), ".code-intel");
14490
15284
  secureMkdir(dir);
14491
15285
  tightenDbFiles(dir);
14492
15286
  } catch {
@@ -14503,10 +15297,10 @@ program.command("health").description("Run code health checks: dead code, circul
14503
15297
  $ code-intel health --json
14504
15298
  $ code-intel health --threshold 80
14505
15299
  `).action(async (targetPath, opts) => {
14506
- const workspaceRoot = path33.resolve(targetPath);
15300
+ const workspaceRoot = path35.resolve(targetPath);
14507
15301
  const dbPath = getDbPath(workspaceRoot);
14508
15302
  const meta = loadMetadata(workspaceRoot);
14509
- if (!meta || !fs33.existsSync(dbPath)) {
15303
+ if (!meta || !fs34.existsSync(dbPath)) {
14510
15304
  console.error(`
14511
15305
  \u2717 ${workspaceRoot} is not indexed.`);
14512
15306
  console.error(" Run `code-intel analyze` first to build the index.\n");
@@ -14591,7 +15385,7 @@ program.command("query").description("Execute a GQL (Graph Query Language) query
14591
15385
  $ code-intel query --list
14592
15386
  $ code-intel query --delete auth-search
14593
15387
  `).action(async (gqlArg, opts) => {
14594
- const workspaceRoot = path33.resolve(opts.path);
15388
+ const workspaceRoot = path35.resolve(opts.path);
14595
15389
  const { saveQuery: saveQuery2, loadQuery: loadQuery2, listQueries: listQueries2, deleteQuery: deleteQuery2 } = await Promise.resolve().then(() => (init_saved_queries(), saved_queries_exports));
14596
15390
  if (opts.list) {
14597
15391
  const queries = listQueries2(workspaceRoot);
@@ -14650,14 +15444,14 @@ program.command("query").description("Execute a GQL (Graph Query Language) query
14650
15444
  }
14651
15445
  gqlInput = content;
14652
15446
  } else if (opts.file) {
14653
- const filePath = path33.resolve(opts.file);
14654
- if (!fs33.existsSync(filePath)) {
15447
+ const filePath = path35.resolve(opts.file);
15448
+ if (!fs34.existsSync(filePath)) {
14655
15449
  console.error(`
14656
15450
  \u2717 File not found: ${filePath}
14657
15451
  `);
14658
15452
  process.exit(1);
14659
15453
  }
14660
- gqlInput = fs33.readFileSync(filePath, "utf-8");
15454
+ gqlInput = fs34.readFileSync(filePath, "utf-8");
14661
15455
  } else if (gqlArg) {
14662
15456
  gqlInput = gqlArg;
14663
15457
  } else {
@@ -14780,7 +15574,7 @@ program.command("pr-impact").description("Compute PR blast radius and risk score
14780
15574
  $ code-intel pr-impact --base main --head HEAD --format sarif
14781
15575
  $ code-intel pr-impact --base main --head HEAD --format json
14782
15576
  `).action(async (opts) => {
14783
- const repoPath = path33.resolve(opts.path ?? ".");
15577
+ const repoPath = path35.resolve(opts.path ?? ".");
14784
15578
  const { execSync: execSync3 } = await import('child_process');
14785
15579
  let diff;
14786
15580
  try {
@@ -14866,6 +15660,242 @@ program.command("pr-impact").description("Compute PR blast radius and risk score
14866
15660
  process.exit(1);
14867
15661
  }
14868
15662
  });
15663
+ program.command("complexity").description("Show complexity hotspots ranked by cyclomatic complexity").argument("[path]", "Path to the repository (default: current directory)", ".").option("--top <n>", "Show top N results (default: 20)", "20").option("--threshold <n>", "Only show results above this cyclomatic threshold").option("--format <fmt>", "Output format: table or json (default: table)", "table").option("--scope <scope>", "Limit to a file path prefix").addHelpText("after", `
15664
+ Loads the knowledge graph and ranks functions/methods by cyclomatic complexity.
15665
+
15666
+ Examples:
15667
+ $ code-intel complexity
15668
+ $ code-intel complexity --top 10
15669
+ $ code-intel complexity --threshold 10
15670
+ $ code-intel complexity --format json
15671
+ `).action(async (targetPath, opts) => {
15672
+ const { graph } = await loadOrAnalyzeWorkspace(targetPath);
15673
+ const { computeComplexity: computeComplexity2 } = await Promise.resolve().then(() => (init_complexity(), complexity_exports));
15674
+ let results = computeComplexity2(graph, opts.scope);
15675
+ if (opts.threshold !== void 0) {
15676
+ const thresh = parseInt(opts.threshold, 10);
15677
+ if (!isNaN(thresh)) results = results.filter((r) => r.cyclomatic > thresh);
15678
+ }
15679
+ const top = parseInt(opts.top, 10);
15680
+ if (!isNaN(top) && top > 0) results = results.slice(0, top);
15681
+ if (opts.format === "json") {
15682
+ console.log(JSON.stringify(results, null, 2));
15683
+ return;
15684
+ }
15685
+ if (results.length === 0) {
15686
+ console.log("\n No complexity results found.\n");
15687
+ return;
15688
+ }
15689
+ console.log("\n \u25C8 Complexity Hotspots\n");
15690
+ const header = ` ${"Rank".padEnd(5)} ${"Symbol".padEnd(28)} ${"File".padEnd(40)} ${"Cyclo".padEnd(7)} ${"Cogn".padEnd(7)} Severity`;
15691
+ console.log(header);
15692
+ console.log(" " + "\u2500".repeat(header.length - 2));
15693
+ for (let i = 0; i < results.length; i++) {
15694
+ const r = results[i];
15695
+ const rank = String(i + 1).padEnd(5);
15696
+ const name = r.name.slice(0, 27).padEnd(28);
15697
+ const file = r.filePath.slice(0, 39).padEnd(40);
15698
+ const cyc = String(r.cyclomatic).padEnd(7);
15699
+ const cog = String(r.cognitive).padEnd(7);
15700
+ console.log(` ${rank} ${name} ${file} ${cyc} ${cog} ${r.severity}`);
15701
+ }
15702
+ console.log("");
15703
+ });
15704
+ program.command("coverage").description("Show untested exported symbols ranked by blast radius").argument("[path]", "Path to the repository (default: current directory)", ".").option("--threshold <pct>", "Exit code 1 when coverage is below this percentage").option("--format <fmt>", "Output format: table or json (default: table)", "table").option("--scope <scope>", "Limit to a file path prefix").addHelpText("after", `
15705
+ Loads the knowledge graph and identifies exported symbols with no test coverage.
15706
+
15707
+ Examples:
15708
+ $ code-intel coverage
15709
+ $ code-intel coverage --threshold 80
15710
+ $ code-intel coverage --format json
15711
+ `).action(async (targetPath, opts) => {
15712
+ const { graph } = await loadOrAnalyzeWorkspace(targetPath);
15713
+ const { computeCoverage: computeCoverage2 } = await Promise.resolve().then(() => (init_test_coverage(), test_coverage_exports));
15714
+ const summary = computeCoverage2(graph, opts.scope);
15715
+ if (opts.format === "json") {
15716
+ console.log(JSON.stringify(summary, null, 2));
15717
+ } else {
15718
+ console.log("\n \u25C8 Test Coverage Gaps\n");
15719
+ console.log(` Coverage: ${summary.testedExported}/${summary.totalExported} exported symbols tested (${summary.coveragePct}%)
15720
+ `);
15721
+ if (summary.untestedByRisk.length === 0) {
15722
+ console.log(" All exported symbols have test coverage.\n");
15723
+ } else {
15724
+ console.log(" Untested exported symbols (by blast radius):\n");
15725
+ const header = ` ${"Symbol".padEnd(28)} ${"File".padEnd(40)} ${"Blast".padEnd(7)} Risk`;
15726
+ console.log(header);
15727
+ console.log(" " + "\u2500".repeat(header.length - 2));
15728
+ for (const r of summary.untestedByRisk) {
15729
+ const name = r.name.slice(0, 27).padEnd(28);
15730
+ const file = r.filePath.slice(0, 39).padEnd(40);
15731
+ const blast = String(r.blastRadius).padEnd(7);
15732
+ console.log(` ${name} ${file} ${blast} ${r.risk}`);
15733
+ }
15734
+ console.log("");
15735
+ }
15736
+ }
15737
+ if (opts.threshold !== void 0) {
15738
+ const thresh = parseInt(opts.threshold, 10);
15739
+ if (!isNaN(thresh) && summary.coveragePct < thresh) {
15740
+ process.exit(1);
15741
+ }
15742
+ }
15743
+ });
15744
+ program.command("secrets").description("Scan for hardcoded secrets in the knowledge graph").argument("[path]", "Path to the repository (default: current directory)", ".").option("--format <fmt>", "Output format: table|json (default: table)", "table").option("--fail-on", "Exit 1 if any findings are found").option("--fix-hint", "Show hint to use process.env.VARIABLE_NAME instead").option("--include-tests", "Include test/spec/fixture files in scan").addHelpText("after", `
15745
+ Scans the indexed knowledge graph for hardcoded secrets (API keys, passwords,
15746
+ tokens, private keys, high-entropy strings).
15747
+
15748
+ Examples:
15749
+ $ code-intel secrets
15750
+ $ code-intel secrets --format json
15751
+ $ code-intel secrets --fail-on
15752
+ $ code-intel secrets --fix-hint
15753
+ `).action(async (targetPath, opts) => {
15754
+ const { graph } = await loadOrAnalyzeWorkspace(targetPath);
15755
+ const { SecretScanner: SecretScanner2 } = await Promise.resolve().then(() => (init_secret_scanner(), secret_scanner_exports));
15756
+ const scanner = new SecretScanner2();
15757
+ const findings = scanner.scan(graph, { includeTestFiles: opts.includeTests });
15758
+ if ((opts.format ?? "table") === "json") {
15759
+ console.log(JSON.stringify({ findings, total: findings.length }, null, 2));
15760
+ } else {
15761
+ if (findings.length === 0) {
15762
+ console.log("\n \u2705 No hardcoded secrets found.\n");
15763
+ } else {
15764
+ console.log(`
15765
+ \u26A0\uFE0F Found ${findings.length} potential secret(s):
15766
+ `);
15767
+ console.log(` ${"FILE".padEnd(50)} ${"LINE".padEnd(6)} ${"SYMBOL".padEnd(30)} PATTERN`);
15768
+ console.log(` ${"\u2500".repeat(100)}`);
15769
+ for (const f of findings) {
15770
+ const line = f.line !== void 0 ? String(f.line) : "?";
15771
+ console.log(` ${f.file.padEnd(50)} ${line.padEnd(6)} ${f.symbol.padEnd(30)} ${f.pattern}`);
15772
+ if (opts.fixHint) {
15773
+ console.log(` \u{1F4A1} Hint: Use process.env.${f.symbol.toUpperCase()} instead of a hardcoded value`);
15774
+ }
15775
+ }
15776
+ console.log("");
15777
+ }
15778
+ }
15779
+ if (opts.failOn && findings.length > 0) {
15780
+ process.exit(1);
15781
+ }
15782
+ });
15783
+ program.command("scan").description("Run security scans: secrets + OWASP vulnerability detection").argument("[path]", "Path to the repository (default: current directory)", ".").option("--type <types>", "Comma-separated detector types: secrets,sql,xss,ssrf,path,cmd").option("--severity <level>", "Minimum severity to report: high|medium|low (default: low)", "low").option("--format <fmt>", "Output format: table|json|sarif (default: table)", "table").option("--fail-on <level>", "Exit 1 if findings at or above this severity: high|medium").option("--exclude <pattern>", "Exclude files matching this pattern").addHelpText("after", `
15784
+ Runs both secret detection and OWASP vulnerability scanning against the
15785
+ indexed knowledge graph.
15786
+
15787
+ Examples:
15788
+ $ code-intel scan
15789
+ $ code-intel scan --type secrets,sql
15790
+ $ code-intel scan --severity high --format json
15791
+ $ code-intel scan --fail-on high
15792
+ `).action(async (targetPath, opts) => {
15793
+ const { graph } = await loadOrAnalyzeWorkspace(targetPath);
15794
+ const { SecretScanner: SecretScanner2 } = await Promise.resolve().then(() => (init_secret_scanner(), secret_scanner_exports));
15795
+ const { VulnerabilityDetector: VulnerabilityDetector2 } = await Promise.resolve().then(() => (init_vulnerability_detector(), vulnerability_detector_exports));
15796
+ const requestedTypes = opts.type ? opts.type.split(",").map((t) => t.trim().toLowerCase()) : null;
15797
+ const minSeverity = (opts.severity ?? "low").toLowerCase();
15798
+ const severityRank = { high: 3, medium: 2, low: 1 };
15799
+ const minRank = severityRank[minSeverity] ?? 1;
15800
+ const allFindings = [];
15801
+ if (!requestedTypes || requestedTypes.includes("secrets")) {
15802
+ const scanner = new SecretScanner2();
15803
+ const secrets = scanner.scan(graph);
15804
+ for (const s of secrets) {
15805
+ allFindings.push({
15806
+ kind: "secret",
15807
+ severity: s.severity,
15808
+ file: s.file,
15809
+ line: s.line,
15810
+ symbol: s.symbol,
15811
+ type: `SECRET:${s.pattern}`,
15812
+ description: `Hardcoded secret pattern: ${s.pattern}`
15813
+ });
15814
+ }
15815
+ }
15816
+ const typeMap = {
15817
+ sql: "SQL_INJECTION",
15818
+ xss: "XSS",
15819
+ ssrf: "SSRF",
15820
+ path: "PATH_TRAVERSAL",
15821
+ cmd: "COMMAND_INJECTION"
15822
+ };
15823
+ const vulnTypes = requestedTypes ? requestedTypes.filter((t) => t in typeMap).map((t) => typeMap[t]) : ["SQL_INJECTION", "XSS", "SSRF", "PATH_TRAVERSAL", "COMMAND_INJECTION"];
15824
+ if (vulnTypes.length > 0 && (!requestedTypes || requestedTypes.some((t) => t in typeMap))) {
15825
+ const detector = new VulnerabilityDetector2();
15826
+ const vulns = detector.detect(graph, { types: vulnTypes });
15827
+ for (const v of vulns) {
15828
+ allFindings.push({
15829
+ kind: "vulnerability",
15830
+ severity: v.severity,
15831
+ file: v.file,
15832
+ line: v.line,
15833
+ symbol: v.symbol,
15834
+ type: v.type,
15835
+ description: v.description,
15836
+ cweId: v.cweId
15837
+ });
15838
+ }
15839
+ }
15840
+ const exclude = opts.exclude;
15841
+ const filtered = allFindings.filter((f) => {
15842
+ if (exclude && f.file.includes(exclude)) return false;
15843
+ const rank = severityRank[f.severity.toLowerCase()] ?? 1;
15844
+ return rank >= minRank;
15845
+ });
15846
+ const format = (opts.format ?? "table").toLowerCase();
15847
+ if (format === "json") {
15848
+ console.log(JSON.stringify({ findings: filtered, total: filtered.length }, null, 2));
15849
+ } else if (format === "sarif") {
15850
+ const sarif = {
15851
+ version: "2.1.0",
15852
+ $schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
15853
+ runs: [{
15854
+ tool: { driver: { name: "code-intel", version: "0.8.0", rules: [] } },
15855
+ results: filtered.map((f) => ({
15856
+ ruleId: f.type,
15857
+ message: { text: f.description },
15858
+ locations: [{
15859
+ physicalLocation: {
15860
+ artifactLocation: { uri: f.file },
15861
+ region: { startLine: f.line ?? 1 }
15862
+ }
15863
+ }],
15864
+ level: f.severity === "HIGH" ? "error" : f.severity === "MEDIUM" ? "warning" : "note"
15865
+ }))
15866
+ }]
15867
+ };
15868
+ console.log(JSON.stringify(sarif, null, 2));
15869
+ } else {
15870
+ if (filtered.length === 0) {
15871
+ console.log("\n \u2705 No security findings.\n");
15872
+ } else {
15873
+ const highCount = filtered.filter((f) => f.severity === "HIGH").length;
15874
+ const medCount = filtered.filter((f) => f.severity === "MEDIUM").length;
15875
+ const lowCount = filtered.filter((f) => f.severity === "LOW").length;
15876
+ console.log(`
15877
+ \u25C8 Security Scan \u2014 ${path35.resolve(targetPath)}
15878
+ `);
15879
+ console.log(` HIGH: ${highCount} MEDIUM: ${medCount} LOW: ${lowCount}
15880
+ `);
15881
+ for (const f of filtered) {
15882
+ const icon = f.severity === "HIGH" ? "\u{1F534}" : f.severity === "MEDIUM" ? "\u{1F7E1}" : "\u{1F7E2}";
15883
+ const cwe = f.cweId ? ` [${f.cweId}]` : "";
15884
+ console.log(` ${icon} ${f.severity.padEnd(8)} ${f.type}${cwe}`);
15885
+ const line = f.line !== void 0 ? `:${f.line}` : "";
15886
+ console.log(` ${f.file}${line} ${f.symbol}`);
15887
+ console.log(` ${f.description}`);
15888
+ console.log("");
15889
+ }
15890
+ }
15891
+ }
15892
+ const failOnLevel = (opts.failOn ?? "").toLowerCase();
15893
+ if (failOnLevel === "high" && filtered.some((f) => f.severity === "HIGH")) {
15894
+ process.exit(1);
15895
+ } else if (failOnLevel === "medium" && filtered.some((f) => f.severity === "HIGH" || f.severity === "MEDIUM")) {
15896
+ process.exit(1);
15897
+ }
15898
+ });
14869
15899
  program.parse();
14870
15900
  //# sourceMappingURL=main.js.map
14871
15901
  //# sourceMappingURL=main.js.map