trace-mcp 1.19.0 → 1.20.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.js CHANGED
@@ -1365,6 +1365,11 @@ var init_symbol_repository = __esm({
1365
1365
  getSymbolById(id) {
1366
1366
  return this._stmts.getSymbolById.get(id);
1367
1367
  }
1368
+ getSymbolChildren(parentId) {
1369
+ return this.db.prepare(
1370
+ "SELECT * FROM symbols WHERE parent_id = ?"
1371
+ ).all(parentId);
1372
+ }
1368
1373
  getSymbolByName(name, kind) {
1369
1374
  if (kind) {
1370
1375
  return this.db.prepare(
@@ -2228,6 +2233,9 @@ var init_store = __esm({
2228
2233
  getSymbolByName(name, kind) {
2229
2234
  return this.symbols.getSymbolByName(name, kind);
2230
2235
  }
2236
+ getSymbolChildren(parentId) {
2237
+ return this.symbols.getSymbolChildren(parentId);
2238
+ }
2231
2239
  getExportedSymbols(filePattern) {
2232
2240
  return this.symbols.getExportedSymbols(filePattern);
2233
2241
  }
@@ -5428,6 +5436,44 @@ function extractInheritanceEdges(bases, classSymbolId) {
5428
5436
  metadata: { base }
5429
5437
  }));
5430
5438
  }
5439
+ function extractNameMainCallees(root) {
5440
+ const callees = [];
5441
+ for (const node of root.namedChildren) {
5442
+ if (node.type !== "if_statement") continue;
5443
+ const condition = node.childForFieldName("condition");
5444
+ if (!condition) continue;
5445
+ if (!isNameMainCondition(condition)) continue;
5446
+ const body = node.childForFieldName("consequence");
5447
+ if (!body) continue;
5448
+ collectCalleeNames(body, callees);
5449
+ }
5450
+ return callees;
5451
+ }
5452
+ function isNameMainCondition(node) {
5453
+ if (node.type !== "comparison_operator") return false;
5454
+ const text = node.text;
5455
+ return text.includes("__name__") && (text.includes('"__main__"') || text.includes("'__main__'"));
5456
+ }
5457
+ function collectCalleeNames(node, out) {
5458
+ for (const child of node.namedChildren) {
5459
+ if (child.type === "expression_statement") {
5460
+ collectCalleeNames(child, out);
5461
+ } else if (child.type === "call") {
5462
+ const fn2 = child.childForFieldName("function");
5463
+ if (fn2) {
5464
+ if (fn2.type === "identifier") {
5465
+ out.push(fn2.text);
5466
+ }
5467
+ const args = child.childForFieldName("arguments");
5468
+ if (args) collectCalleeNames(args, out);
5469
+ }
5470
+ } else if (child.type === "function_definition" || child.type === "class_definition") {
5471
+ continue;
5472
+ } else {
5473
+ collectCalleeNames(child, out);
5474
+ }
5475
+ }
5476
+ }
5431
5477
  function extractTypeCheckingImports(root) {
5432
5478
  const edges = [];
5433
5479
  for (const node of root.namedChildren) {
@@ -6132,6 +6178,29 @@ function extractIterableStrings(node) {
6132
6178
  }
6133
6179
  function inferLocalTypes(body) {
6134
6180
  const types = /* @__PURE__ */ new Map();
6181
+ const funcDef = body.parent;
6182
+ if (funcDef && (funcDef.type === "function_definition" || funcDef.type === "decorated_definition")) {
6183
+ const defNode = funcDef.type === "decorated_definition" ? funcDef.namedChildren.find((c) => c.type === "function_definition") : funcDef;
6184
+ if (defNode) {
6185
+ const params = defNode.childForFieldName("parameters");
6186
+ if (params) {
6187
+ for (const param of params.namedChildren) {
6188
+ if (param.type === "typed_parameter" || param.type === "typed_default_parameter") {
6189
+ const nameNode = param.childForFieldName("name") ?? param.namedChildren.find((c) => c.type === "identifier");
6190
+ const typeNode = param.childForFieldName("type");
6191
+ if (nameNode && typeNode) {
6192
+ const paramName = nameNode.text;
6193
+ if (paramName === "self" || paramName === "cls") continue;
6194
+ const typeName = extractParamAnnotationType(typeNode);
6195
+ if (typeName) {
6196
+ types.set(paramName, { type: typeName });
6197
+ }
6198
+ }
6199
+ }
6200
+ }
6201
+ }
6202
+ }
6203
+ }
6135
6204
  for (const child of body.namedChildren) {
6136
6205
  if (child.type === "expression_statement") {
6137
6206
  const expr = child.namedChildren[0];
@@ -6193,6 +6262,34 @@ function extractAnnotationType(typeNode) {
6193
6262
  }
6194
6263
  return null;
6195
6264
  }
6265
+ function extractParamAnnotationType(typeNode) {
6266
+ if (typeNode.type === "type") {
6267
+ return extractAnnotationType(typeNode);
6268
+ }
6269
+ if (typeNode.type === "identifier") {
6270
+ const name = typeNode.text;
6271
+ if (name[0] >= "A" && name[0] <= "Z") return name;
6272
+ return null;
6273
+ }
6274
+ if (typeNode.type === "attribute") {
6275
+ const attr = typeNode.childForFieldName("attribute");
6276
+ if (attr && attr.text[0] >= "A" && attr.text[0] <= "Z") return attr.text;
6277
+ return null;
6278
+ }
6279
+ if (typeNode.type === "subscript" || typeNode.type === "generic_type") {
6280
+ for (const child of typeNode.namedChildren) {
6281
+ if (child.type === "identifier" && child.text[0] >= "A" && child.text[0] <= "Z") {
6282
+ const WRAPPER_TYPES = /* @__PURE__ */ new Set(["Optional", "List", "Set", "Dict", "Tuple", "Type", "Sequence", "Iterable"]);
6283
+ if (!WRAPPER_TYPES.has(child.text)) return child.text;
6284
+ }
6285
+ }
6286
+ for (const child of typeNode.namedChildren) {
6287
+ const inner = extractParamAnnotationType(child);
6288
+ if (inner) return inner;
6289
+ }
6290
+ }
6291
+ return extractAnnotationType(typeNode);
6292
+ }
6196
6293
  function parseCallTarget(fn2, line, localTypes) {
6197
6294
  if (fn2.type === "identifier") {
6198
6295
  const name = fn2.text;
@@ -6447,6 +6544,17 @@ var init_python = __esm({
6447
6544
  sym.metadata.exported = true;
6448
6545
  }
6449
6546
  }
6547
+ const nameMainCallees = extractNameMainCallees(root);
6548
+ if (nameMainCallees.length > 0) {
6549
+ const calleeSet = new Set(nameMainCallees);
6550
+ for (const sym of symbols) {
6551
+ if (sym.parentSymbolId) continue;
6552
+ if (calleeSet.has(sym.name)) {
6553
+ sym.metadata = sym.metadata ?? {};
6554
+ sym.metadata.is_entry_point = "name_main";
6555
+ }
6556
+ }
6557
+ }
6450
6558
  const moduleLevelCalls = extractCallSites(root);
6451
6559
  if (moduleLevelCalls.length > 0) {
6452
6560
  const moduleSymbolId = makeSymbolId3(filePath, "<module>", "function");
@@ -42528,11 +42636,17 @@ function resolvePythonCallEdges(state) {
42528
42636
  if (!targetSymbol) continue;
42529
42637
  const targetNodeId = symbolNodeMap.get(targetSymbol.id);
42530
42638
  if (targetNodeId == null || targetNodeId === sourceNodeId) continue;
42639
+ const edgeMeta = { callee: call.calleeName, line: call.line };
42640
+ if (call.receiverType) edgeMeta.receiver_type = call.receiverType;
42641
+ else if (call.receiverAssignedFrom) {
42642
+ const inferredType = returnTypeIndex.get(call.receiverAssignedFrom);
42643
+ if (inferredType) edgeMeta.receiver_type = inferredType;
42644
+ }
42531
42645
  insertStmt.run(
42532
42646
  sourceNodeId,
42533
42647
  targetNodeId,
42534
42648
  callsEdgeType.id,
42535
- JSON.stringify({ callee: call.calleeName, line: call.line })
42649
+ JSON.stringify(edgeMeta)
42536
42650
  );
42537
42651
  created++;
42538
42652
  }
@@ -42737,6 +42851,13 @@ function resolveMethodOnClass(typeName, calleeName, fileSymbols, fileImports, na
42737
42851
  }
42738
42852
  }
42739
42853
  }
42854
+ const methodCandidates = nameIndex.get(calleeName);
42855
+ if (methodCandidates) {
42856
+ const match = methodCandidates.find(
42857
+ (s) => (s.kind === "method" || s.kind === "function") && s.parent_symbol_id != null
42858
+ );
42859
+ if (match) return match;
42860
+ }
42740
42861
  return null;
42741
42862
  }
42742
42863
  function buildFileImportMap(state) {
@@ -42876,6 +42997,10 @@ function resolveTestCoversEdges(state) {
42876
42997
  const targetRefs = store.getNodeRefsBatch(targetNodeIds);
42877
42998
  const targetFileRefIds = [...targetRefs.values()].filter((r) => r.nodeType === "file").map((r) => r.refId);
42878
42999
  const targetFileMap = store.getFilesByIds(targetFileRefIds);
43000
+ const targetSymbolRefIds = [...targetRefs.values()].filter((r) => r.nodeType === "symbol").map((r) => r.refId);
43001
+ const targetSymbolMap = store.getSymbolsByIds(targetSymbolRefIds);
43002
+ const symbolFileIds = [...new Set([...targetSymbolMap.values()].map((s) => s.file_id))];
43003
+ const symbolFileMap = store.getFilesByIds(symbolFileIds);
42879
43004
  const testCoversType = store.db.prepare("SELECT id FROM edge_types WHERE name = ?").get("test_covers");
42880
43005
  if (!testCoversType) return;
42881
43006
  let created = 0;
@@ -42890,18 +43015,32 @@ function resolveTestCoversEdges(state) {
42890
43015
  if (edge.edge_type_name !== "imports") continue;
42891
43016
  if (!testNodeToFile.has(edge.source_node_id)) continue;
42892
43017
  const targetRef = targetRefs.get(edge.target_node_id);
42893
- if (!targetRef || targetRef.nodeType !== "file") continue;
42894
- const targetFile = targetFileMap.get(targetRef.refId);
42895
- if (!targetFile) continue;
42896
- if (TEST_PATH_RE.test(targetFile.path)) continue;
43018
+ if (!targetRef) continue;
42897
43019
  const testFile = testNodeToFile.get(edge.source_node_id);
42898
- insertStmt.run(
42899
- edge.source_node_id,
42900
- edge.target_node_id,
42901
- testCoversType.id,
42902
- JSON.stringify({ test_file: testFile.path })
42903
- );
42904
- created++;
43020
+ if (targetRef.nodeType === "file") {
43021
+ const targetFile = targetFileMap.get(targetRef.refId);
43022
+ if (!targetFile) continue;
43023
+ if (TEST_PATH_RE.test(targetFile.path)) continue;
43024
+ insertStmt.run(
43025
+ edge.source_node_id,
43026
+ edge.target_node_id,
43027
+ testCoversType.id,
43028
+ JSON.stringify({ test_file: testFile.path })
43029
+ );
43030
+ created++;
43031
+ } else if (targetRef.nodeType === "symbol") {
43032
+ const targetSymbol = targetSymbolMap.get(targetRef.refId);
43033
+ if (!targetSymbol) continue;
43034
+ const parentFile = symbolFileMap.get(targetSymbol.file_id);
43035
+ if (!parentFile || TEST_PATH_RE.test(parentFile.path)) continue;
43036
+ insertStmt.run(
43037
+ edge.source_node_id,
43038
+ edge.target_node_id,
43039
+ testCoversType.id,
43040
+ JSON.stringify({ test_file: testFile.path, target_symbol: targetSymbol.symbol_id })
43041
+ );
43042
+ created++;
43043
+ }
42905
43044
  }
42906
43045
  })();
42907
43046
  if (created > 0) {
@@ -89601,7 +89740,7 @@ import https from "https";
89601
89740
  import { spawnSync } from "child_process";
89602
89741
  import path65 from "path";
89603
89742
  import fs66 from "fs";
89604
- var CURRENT_VERSION = true ? "1.19.0" : "0.0.0-dev";
89743
+ var CURRENT_VERSION = true ? "1.20.0" : "0.0.0-dev";
89605
89744
  var UPDATE_CACHE_PATH = path65.join(TRACE_MCP_HOME, "update-check.json");
89606
89745
  function readCache() {
89607
89746
  try {
@@ -93266,6 +93405,119 @@ function resolveSymbolInput(store, opts) {
93266
93405
  return { symbol: sym, file, resolved_via: resolvedVia };
93267
93406
  }
93268
93407
 
93408
+ // src/tools/shared/cha.ts
93409
+ var HERITAGE_EDGE_TYPES = [
93410
+ "ts_extends",
93411
+ "ts_implements",
93412
+ // TypeScript
93413
+ "extends",
93414
+ "implements",
93415
+ // PHP
93416
+ "py_inherits"
93417
+ // Python (fallback)
93418
+ ];
93419
+ function expandMethodViaCha(store, symbol, maxDepth = 10) {
93420
+ if (symbol.kind !== "method") {
93421
+ const nodeId = store.getNodeId("symbol", symbol.id);
93422
+ if (nodeId == null) return [];
93423
+ return [{ symbol, nodeId, relation: "self" }];
93424
+ }
93425
+ const methodName = symbol.name;
93426
+ const parentClass = symbol.parent_id != null ? store.getSymbolById(symbol.parent_id) : null;
93427
+ if (!parentClass || parentClass.kind !== "class" && parentClass.kind !== "interface") {
93428
+ const nodeId = store.getNodeId("symbol", symbol.id);
93429
+ if (nodeId == null) return [];
93430
+ return [{ symbol, nodeId, relation: "self" }];
93431
+ }
93432
+ const results = [];
93433
+ const selfNodeId = store.getNodeId("symbol", symbol.id);
93434
+ if (selfNodeId != null) {
93435
+ results.push({ symbol, nodeId: selfNodeId, relation: "self" });
93436
+ }
93437
+ const ancestorClassIds = /* @__PURE__ */ new Set();
93438
+ const descendantClassIds = /* @__PURE__ */ new Set();
93439
+ collectAncestors(store, parentClass, ancestorClassIds, /* @__PURE__ */ new Set(), maxDepth);
93440
+ collectDescendants(store, parentClass.name, descendantClassIds, /* @__PURE__ */ new Set(), maxDepth);
93441
+ for (const classId of ancestorClassIds) {
93442
+ const method = findMethodOnClass(store, classId, methodName);
93443
+ if (method && method.id !== symbol.id) {
93444
+ const nodeId = store.getNodeId("symbol", method.id);
93445
+ if (nodeId != null) {
93446
+ results.push({ symbol: method, nodeId, relation: "ancestor_method" });
93447
+ }
93448
+ }
93449
+ }
93450
+ for (const classId of descendantClassIds) {
93451
+ const method = findMethodOnClass(store, classId, methodName);
93452
+ if (method && method.id !== symbol.id) {
93453
+ const nodeId = store.getNodeId("symbol", method.id);
93454
+ if (nodeId != null) {
93455
+ results.push({ symbol: method, nodeId, relation: "descendant_method" });
93456
+ }
93457
+ }
93458
+ }
93459
+ return results;
93460
+ }
93461
+ function getChaNodeIds(store, symbol) {
93462
+ return expandMethodViaCha(store, symbol).map((m) => m.nodeId);
93463
+ }
93464
+ function collectAncestors(store, classSymbol, result, visited, depth) {
93465
+ if (depth <= 0 || visited.has(classSymbol.id)) return;
93466
+ visited.add(classSymbol.id);
93467
+ const nodeId = store.getNodeId("symbol", classSymbol.id);
93468
+ if (nodeId != null) {
93469
+ const outgoing = store.getOutgoingEdges(nodeId);
93470
+ for (const edge of outgoing) {
93471
+ if (!HERITAGE_EDGE_TYPES.includes(edge.edge_type_name)) continue;
93472
+ const targetRef = store.getNodeRef(edge.target_node_id);
93473
+ if (!targetRef || targetRef.nodeType !== "symbol") continue;
93474
+ result.add(targetRef.refId);
93475
+ const parentSym = store.getSymbolById(targetRef.refId);
93476
+ if (parentSym) {
93477
+ collectAncestors(store, parentSym, result, visited, depth - 1);
93478
+ }
93479
+ }
93480
+ }
93481
+ if (classSymbol.metadata) {
93482
+ try {
93483
+ const meta = JSON.parse(classSymbol.metadata);
93484
+ const parentNames = [];
93485
+ const ext = meta["extends"];
93486
+ if (Array.isArray(ext)) parentNames.push(...ext.filter((n) => typeof n === "string"));
93487
+ else if (typeof ext === "string") parentNames.push(ext);
93488
+ const impl = meta["implements"];
93489
+ if (Array.isArray(impl)) parentNames.push(...impl.filter((n) => typeof n === "string"));
93490
+ const bases = meta["bases"];
93491
+ if (Array.isArray(bases)) parentNames.push(...bases.filter((n) => typeof n === "string"));
93492
+ for (const name of parentNames) {
93493
+ const shortName3 = name.includes(".") ? name.split(".").pop() : name;
93494
+ const parentSym = store.getSymbolByName(shortName3, "class") ?? store.getSymbolByName(shortName3, "interface");
93495
+ if (parentSym && !result.has(parentSym.id)) {
93496
+ result.add(parentSym.id);
93497
+ collectAncestors(store, parentSym, result, visited, depth - 1);
93498
+ }
93499
+ }
93500
+ } catch {
93501
+ }
93502
+ }
93503
+ }
93504
+ function collectDescendants(store, className, result, visited, depth) {
93505
+ if (depth <= 0 || visited.has(className)) return;
93506
+ visited.add(className);
93507
+ const implementors = store.findImplementors(className);
93508
+ for (const impl of implementors) {
93509
+ if (result.has(impl.id)) continue;
93510
+ result.add(impl.id);
93511
+ collectDescendants(store, impl.name, result, visited, depth - 1);
93512
+ }
93513
+ }
93514
+ function findMethodOnClass(store, classId, methodName) {
93515
+ const children = store.getSymbolChildren(classId);
93516
+ return children.find(
93517
+ (s) => s.name === methodName && (s.kind === "method" || s.kind === "function")
93518
+ ) ?? null;
93519
+ }
93520
+
93269
93521
  // src/tools/analysis/impact.ts
93270
93522
  function getModule(filePath, depth = 2) {
93271
93523
  const parts = filePath.split("/");
@@ -93422,8 +93674,13 @@ function getChangeImpact(store, opts, depth = 3, maxDependents = 200, cwd) {
93422
93674
  const sym = store.getSymbolBySymbolId(sid);
93423
93675
  if (!sym) continue;
93424
93676
  if (!firstSym) firstSym = sym;
93425
- const nid = store.getNodeId("symbol", sym.id);
93426
- if (nid != null) startNodeIds.push(nid);
93677
+ const chaNodeIds = getChaNodeIds(store, sym);
93678
+ if (chaNodeIds.length > 0) {
93679
+ startNodeIds.push(...chaNodeIds);
93680
+ } else {
93681
+ const nid = store.getNodeId("symbol", sym.id);
93682
+ if (nid != null) startNodeIds.push(nid);
93683
+ }
93427
93684
  }
93428
93685
  if (!firstSym) {
93429
93686
  return err(notFound(opts.symbolIds[0]));
@@ -93440,8 +93697,14 @@ function getChangeImpact(store, opts, depth = 3, maxDependents = 200, cwd) {
93440
93697
  return err(notFound(opts.symbolId ?? opts.fqn ?? "unknown"));
93441
93698
  }
93442
93699
  const sym = resolved.symbol;
93443
- const nodeId = store.getNodeId("symbol", sym.id);
93444
- if (nodeId != null) startNodeIds.push(nodeId);
93700
+ const chaNodeIds = getChaNodeIds(store, sym);
93701
+ if (chaNodeIds.length > 0) {
93702
+ startNodeIds.push(...chaNodeIds);
93703
+ } else {
93704
+ const nodeId = store.getNodeId("symbol", sym.id);
93705
+ if (nodeId != null) startNodeIds.push(nodeId);
93706
+ }
93707
+ startNodeIds = [...new Set(startNodeIds)];
93445
93708
  targetPath = resolved.file.path;
93446
93709
  targetSymbolId = sym.symbol_id;
93447
93710
  targetSymbolName = sym.name;
@@ -98470,8 +98733,41 @@ function findReferences(store, opts) {
98470
98733
  if (nodeId === void 0) {
98471
98734
  return ok65({ target: targetMeta, references: [], total: 0 });
98472
98735
  }
98473
- const incomingEdges = store.getIncomingEdges(nodeId);
98474
- const sourceNodeIds = incomingEdges.map((e) => e.source_node_id);
98736
+ let chaExpansion;
98737
+ let allTargetNodeIds = [nodeId];
98738
+ if (opts.symbolId || opts.fqn) {
98739
+ const resolved = resolveSymbolInput(store, opts);
98740
+ if (resolved) {
98741
+ const chaMatches = expandMethodViaCha(store, resolved.symbol);
98742
+ if (chaMatches.length > 1) {
98743
+ allTargetNodeIds = chaMatches.map((m) => m.nodeId);
98744
+ chaExpansion = chaMatches.filter((m) => m.relation !== "self").map((m) => ({
98745
+ symbol_id: m.symbol.symbol_id,
98746
+ name: m.symbol.name,
98747
+ relation: m.relation
98748
+ }));
98749
+ }
98750
+ }
98751
+ }
98752
+ const allIncomingEdges = [];
98753
+ const seenEdgeKeys = /* @__PURE__ */ new Set();
98754
+ for (const targetNid of allTargetNodeIds) {
98755
+ const edges = store.getIncomingEdges(targetNid);
98756
+ const isChaTarget = targetNid !== nodeId;
98757
+ for (const edge of edges) {
98758
+ const key = `${edge.source_node_id}:${edge.target_node_id}:${edge.edge_type_id}`;
98759
+ if (seenEdgeKeys.has(key)) continue;
98760
+ seenEdgeKeys.add(key);
98761
+ allIncomingEdges.push({
98762
+ edge,
98763
+ via_cha: isChaTarget ? chaExpansion?.find((c) => {
98764
+ const ref = store.getNodeRef(targetNid);
98765
+ return ref && ref.nodeType === "symbol" ? store.getSymbolById(ref.refId)?.symbol_id === c.symbol_id : false;
98766
+ })?.name : void 0
98767
+ });
98768
+ }
98769
+ }
98770
+ const sourceNodeIds = allIncomingEdges.map((e) => e.edge.source_node_id);
98475
98771
  const nodeRefs = store.getNodeRefsBatch(sourceNodeIds);
98476
98772
  const symbolRefIds = [];
98477
98773
  const fileRefIds = [];
@@ -98485,7 +98781,7 @@ function findReferences(store, opts) {
98485
98781
  const allFileIds = [.../* @__PURE__ */ new Set([...fileRefIds, ...symFileIds])];
98486
98782
  const fileMap = allFileIds.length > 0 ? store.getFilesByIds(allFileIds) : /* @__PURE__ */ new Map();
98487
98783
  const references = [];
98488
- for (const edge of incomingEdges) {
98784
+ for (const { edge, via_cha } of allIncomingEdges) {
98489
98785
  const sourceRef = nodeRefs.get(edge.source_node_id);
98490
98786
  if (!sourceRef) continue;
98491
98787
  let symbol = null;
@@ -98507,13 +98803,17 @@ function findReferences(store, opts) {
98507
98803
  } else {
98508
98804
  filePath = `[${sourceRef.nodeType}:${sourceRef.refId}]`;
98509
98805
  }
98510
- references.push({
98806
+ const ref = {
98511
98807
  edge_type: edge.edge_type_name,
98512
98808
  symbol,
98513
98809
  file: filePath
98514
- });
98810
+ };
98811
+ if (via_cha) ref.via_cha = via_cha;
98812
+ references.push(ref);
98515
98813
  }
98516
- return ok65({ target: targetMeta, references, total: references.length });
98814
+ const result = { target: targetMeta, references, total: references.length };
98815
+ if (chaExpansion && chaExpansion.length > 0) result.cha_expansion = chaExpansion;
98816
+ return ok65(result);
98517
98817
  }
98518
98818
 
98519
98819
  // src/tools/framework/call-graph.ts
@@ -98559,9 +98859,19 @@ function getCallGraph(store, opts, depth = 2) {
98559
98859
  resolution_tiers: { lsp_resolved: 0, ast_resolved: 0, ast_inferred: 0, text_matched: 0 }
98560
98860
  });
98561
98861
  }
98862
+ const chaMatches = expandMethodViaCha(store, symbol);
98863
+ const chaAliasNodeIds = chaMatches.filter((m) => m.relation !== "self").map((m) => m.nodeId);
98562
98864
  const edgeTypesUsed = /* @__PURE__ */ new Set();
98563
98865
  const visited = /* @__PURE__ */ new Set();
98564
- const { node: rootNode, tiers } = buildCallNode(store, symbol.id, nodeId, depth, visited, edgeTypesUsed);
98866
+ const { node: rootNode, tiers } = buildCallNode(
98867
+ store,
98868
+ symbol.id,
98869
+ nodeId,
98870
+ depth,
98871
+ visited,
98872
+ edgeTypesUsed,
98873
+ chaAliasNodeIds
98874
+ );
98565
98875
  return ok66({
98566
98876
  root: rootNode,
98567
98877
  edge_types_used: [...edgeTypesUsed],
@@ -98569,12 +98879,14 @@ function getCallGraph(store, opts, depth = 2) {
98569
98879
  resolution_tiers: tiers
98570
98880
  });
98571
98881
  }
98572
- function buildCallNode(store, rootSymbolId, rootNodeId, maxDepth, _visited, edgeTypesUsed) {
98882
+ function buildCallNode(store, rootSymbolId, rootNodeId, maxDepth, _visited, edgeTypesUsed, chaAliasNodeIds = []) {
98573
98883
  const tiers = { lsp_resolved: 0, ast_resolved: 0, ast_inferred: 0, text_matched: 0 };
98574
98884
  const nodeInfoMap = /* @__PURE__ */ new Map();
98575
98885
  const visited = /* @__PURE__ */ new Set();
98576
- let frontier = [rootNodeId];
98886
+ const chaAliasSet = new Set(chaAliasNodeIds);
98887
+ let frontier = [rootNodeId, ...chaAliasNodeIds];
98577
98888
  visited.add(rootNodeId);
98889
+ for (const alias of chaAliasNodeIds) visited.add(alias);
98578
98890
  nodeInfoMap.set(rootNodeId, { nodeId: rootNodeId, symbolRefId: rootSymbolId, outgoing: [], incoming: [] });
98579
98891
  for (let d = 0; d < maxDepth; d++) {
98580
98892
  if (frontier.length === 0) break;
@@ -98583,15 +98895,16 @@ function buildCallNode(store, rootSymbolId, rootNodeId, maxDepth, _visited, edge
98583
98895
  for (const edge of batchEdges) {
98584
98896
  if (!CALL_EDGE_TYPES2.has(edge.edge_type_name)) continue;
98585
98897
  edgeTypesUsed.add(edge.edge_type_name);
98586
- const pivotInfo = nodeInfoMap.get(edge.pivot_node_id);
98898
+ const effectivePivot = chaAliasSet.has(edge.pivot_node_id) ? rootNodeId : edge.pivot_node_id;
98899
+ const pivotInfo = nodeInfoMap.get(effectivePivot);
98587
98900
  if (!pivotInfo) continue;
98588
98901
  const resolution = inferResolution(edge);
98589
98902
  tiers[resolution]++;
98590
- if (edge.source_node_id === edge.pivot_node_id && !visited.has(edge.target_node_id)) {
98903
+ if (edge.source_node_id === edge.pivot_node_id && !visited.has(edge.target_node_id) && !chaAliasSet.has(edge.target_node_id)) {
98591
98904
  pivotInfo.outgoing.push({ nodeId: edge.target_node_id, edgeType: edge.edge_type_name, resolution });
98592
98905
  newNeighbors.add(edge.target_node_id);
98593
98906
  }
98594
- if (edge.target_node_id === edge.pivot_node_id && !visited.has(edge.source_node_id)) {
98907
+ if (edge.target_node_id === edge.pivot_node_id && !visited.has(edge.source_node_id) && !chaAliasSet.has(edge.source_node_id)) {
98595
98908
  pivotInfo.incoming.push({ nodeId: edge.source_node_id, edgeType: edge.edge_type_name, resolution });
98596
98909
  newNeighbors.add(edge.source_node_id);
98597
98910
  }
@@ -98884,26 +99197,31 @@ import { ok as ok68, err as err43 } from "neverthrow";
98884
99197
  function getTestsFor(store, opts) {
98885
99198
  let targetFile;
98886
99199
  let targetSymbolId;
98887
- let nodeId;
99200
+ let symbolNodeId;
99201
+ let fileNodeId;
98888
99202
  if (opts.symbolId || opts.fqn) {
98889
99203
  const resolved = resolveSymbolInput(store, opts);
98890
99204
  if (!resolved) return err43(notFound(opts.symbolId ?? opts.fqn ?? "unknown"));
98891
99205
  const symbol = resolved.symbol;
98892
99206
  targetSymbolId = symbol.symbol_id;
98893
99207
  targetFile = resolved.file.path;
98894
- nodeId = store.getNodeId("symbol", symbol.id);
99208
+ symbolNodeId = store.getNodeId("symbol", symbol.id);
99209
+ fileNodeId = store.getNodeId("file", resolved.file.id);
98895
99210
  } else if (opts.filePath) {
98896
99211
  const f = store.getFile(opts.filePath);
98897
99212
  if (!f) return err43(notFound(opts.filePath));
98898
99213
  targetFile = f.path;
98899
- nodeId = store.getNodeId("file", f.id);
99214
+ fileNodeId = store.getNodeId("file", f.id);
98900
99215
  } else {
98901
99216
  return err43(notFound("provide symbol_id, fqn, or file_path"));
98902
99217
  }
98903
99218
  const tests = [];
98904
99219
  const seen = /* @__PURE__ */ new Set();
98905
- if (nodeId !== void 0) {
98906
- const incoming = store.getIncomingEdges(nodeId);
99220
+ const nodeIdsToCheck = /* @__PURE__ */ new Set();
99221
+ if (symbolNodeId !== void 0) nodeIdsToCheck.add(symbolNodeId);
99222
+ if (fileNodeId !== void 0) nodeIdsToCheck.add(fileNodeId);
99223
+ for (const nid of nodeIdsToCheck) {
99224
+ const incoming = store.getIncomingEdges(nid);
98907
99225
  for (const edge of incoming) {
98908
99226
  if (edge.edge_type_name !== "test_covers") continue;
98909
99227
  const ref = store.getNodeRef(edge.source_node_id);
@@ -99459,6 +99777,55 @@ function walkDescendants(store, name, result, visited, depth) {
99459
99777
  walkDescendants(store, row.name, node.children, visited, depth - 1);
99460
99778
  }
99461
99779
  }
99780
+ function collectPyprojectEntryPoints(store) {
99781
+ const names = /* @__PURE__ */ new Set();
99782
+ const allFiles = store.getAllFiles();
99783
+ const pyprojectFile = allFiles.find((f) => f.path.endsWith("pyproject.toml"));
99784
+ if (!pyprojectFile) return names;
99785
+ const symbols = store.getSymbolsByFile(pyprojectFile.id);
99786
+ for (const sym of symbols) {
99787
+ if (!sym.metadata) continue;
99788
+ try {
99789
+ const meta = typeof sym.metadata === "string" ? JSON.parse(sym.metadata) : sym.metadata;
99790
+ const section = typeof meta.section === "string" ? meta.section : "";
99791
+ const isScriptSection = /(?:project\.(?:scripts|gui-scripts)|console_scripts|gui_scripts)/.test(section);
99792
+ if (!isScriptSection) continue;
99793
+ const value = typeof meta.value === "string" ? meta.value : sym.signature ?? "";
99794
+ const colonIdx = value.indexOf(":");
99795
+ if (colonIdx >= 0) {
99796
+ const funcPart = value.slice(colonIdx + 1).trim();
99797
+ const dotIdx = funcPart.lastIndexOf(".");
99798
+ names.add(dotIdx >= 0 ? funcPart.slice(dotIdx + 1) : funcPart);
99799
+ }
99800
+ } catch {
99801
+ }
99802
+ }
99803
+ const setupFile = allFiles.find((f) => f.path.endsWith("setup.py") || f.path.endsWith("setup.cfg"));
99804
+ if (setupFile) {
99805
+ const setupSymbols = store.getSymbolsByFile(setupFile.id);
99806
+ for (const sym of setupSymbols) {
99807
+ if (!sym.metadata) continue;
99808
+ try {
99809
+ const meta = typeof sym.metadata === "string" ? JSON.parse(sym.metadata) : sym.metadata;
99810
+ if (meta.console_scripts || meta.gui_scripts) {
99811
+ const scripts = meta.console_scripts ?? meta.gui_scripts;
99812
+ if (Array.isArray(scripts)) {
99813
+ for (const entry of scripts) {
99814
+ const colonIdx = entry.indexOf(":");
99815
+ if (colonIdx >= 0) {
99816
+ const funcPart = entry.slice(colonIdx + 1).trim();
99817
+ const dotIdx = funcPart.lastIndexOf(".");
99818
+ names.add(dotIdx >= 0 ? funcPart.slice(dotIdx + 1) : funcPart);
99819
+ }
99820
+ }
99821
+ }
99822
+ }
99823
+ } catch {
99824
+ }
99825
+ }
99826
+ }
99827
+ return names;
99828
+ }
99462
99829
  function getDeadExports(store, filePattern) {
99463
99830
  const exported = store.getExportedSymbols(filePattern).filter((s) => !TEST_FIXTURE_RE.test(s.file_path)).filter((s) => !TEST_FILE_RE.test(s.file_path));
99464
99831
  const importedNames = /* @__PURE__ */ new Set();
@@ -99478,9 +99845,18 @@ function getDeadExports(store, filePattern) {
99478
99845
  }
99479
99846
  }
99480
99847
  }
99848
+ const pyprojectEntryNames = collectPyprojectEntryPoints(store);
99481
99849
  const dead = [];
99482
99850
  for (const sym of exported) {
99483
99851
  if (sym.kind === "method") continue;
99852
+ if (sym.metadata) {
99853
+ try {
99854
+ const meta = typeof sym.metadata === "string" ? JSON.parse(sym.metadata) : sym.metadata;
99855
+ if (meta.is_entry_point) continue;
99856
+ } catch {
99857
+ }
99858
+ }
99859
+ if (pyprojectEntryNames.has(sym.name)) continue;
99484
99860
  if (!importedNames.has(sym.name)) {
99485
99861
  dead.push({
99486
99862
  symbol_id: sym.symbol_id,
@@ -119137,7 +119513,7 @@ var DecisionStore = class {
119137
119513
  };
119138
119514
 
119139
119515
  // src/server/server.ts
119140
- var PKG_VERSION = true ? "1.19.0" : "0.0.0-dev";
119516
+ var PKG_VERSION = true ? "1.20.0" : "0.0.0-dev";
119141
119517
  function j2(value) {
119142
119518
  return JSON.stringify(value, (_key, val) => val === null || val === void 0 ? void 0 : val);
119143
119519
  }
@@ -124980,7 +125356,7 @@ var ProjectManager = class {
124980
125356
 
124981
125357
  // src/cli.ts
124982
125358
  init_global();
124983
- var PKG_VERSION2 = true ? "1.19.0" : "0.0.0-dev";
125359
+ var PKG_VERSION2 = true ? "1.20.0" : "0.0.0-dev";
124984
125360
  function registerDefaultPlugins2(registry) {
124985
125361
  for (const p5 of createAllLanguagePlugins()) registry.registerLanguagePlugin(p5);
124986
125362
  for (const p5 of createAllIntegrationPlugins()) registry.registerFrameworkPlugin(p5);