codecompass-mcp 3.0.1__tar.gz → 3.1.0__tar.gz

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.
Files changed (44) hide show
  1. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/PKG-INFO +1 -1
  2. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/codecompass_mcp.egg-info/PKG-INFO +1 -1
  3. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/ingestion/code_parser.py +74 -9
  4. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/pyproject.toml +1 -1
  5. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/tests/test_accuracy_fixes.py +15 -0
  6. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/LICENSE +0 -0
  7. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/README.md +0 -0
  8. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/codecompass_mcp.egg-info/SOURCES.txt +0 -0
  9. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/codecompass_mcp.egg-info/dependency_links.txt +0 -0
  10. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/codecompass_mcp.egg-info/entry_points.txt +0 -0
  11. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/codecompass_mcp.egg-info/requires.txt +0 -0
  12. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/codecompass_mcp.egg-info/top_level.txt +0 -0
  13. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/config.py +0 -0
  14. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/graph/__init__.py +0 -0
  15. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/graph/cli.py +0 -0
  16. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/graph/code_graph_client.py +0 -0
  17. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/graph/code_query_cli.py +0 -0
  18. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/graph/setup.py +0 -0
  19. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/ingestion/__init__.py +0 -0
  20. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/ingestion/chunker.py +0 -0
  21. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/ingestion/code_normalizer.py +0 -0
  22. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/ingestion/description_enricher.py +0 -0
  23. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/ingestion/entity_resolver.py +0 -0
  24. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/ingestion/file_watcher.py +0 -0
  25. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/ingestion/graph_writer.py +0 -0
  26. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/ingestion/hierarchy_builder.py +0 -0
  27. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/ingestion/reader_agent.py +0 -0
  28. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/ingestion/source_context.py +0 -0
  29. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/main.py +0 -0
  30. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/mcp_server.py +0 -0
  31. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/models/__init__.py +0 -0
  32. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/models/code_types.py +0 -0
  33. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/models/types.py +0 -0
  34. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/setup.cfg +0 -0
  35. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/tests/test_batch_impact.py +0 -0
  36. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/tests/test_blast_radius.py +0 -0
  37. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/tests/test_code_parser.py +0 -0
  38. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/tests/test_description_enricher.py +0 -0
  39. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/tests/test_ingestion.py +0 -0
  40. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/tests/test_lit_css_parser.py +0 -0
  41. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/tests/test_mcp_server.py +0 -0
  42. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/tests/test_php_parser.py +0 -0
  43. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/utils/__init__.py +0 -0
  44. {codecompass_mcp-3.0.1 → codecompass_mcp-3.1.0}/utils/formatting.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codecompass-mcp
3
- Version: 3.0.1
3
+ Version: 3.1.0
4
4
  Summary: Structural code context for AI coding agents — a local code knowledge graph for blast radius, impact, deps, dead code, and flow tracing
5
5
  Author: CodeCompass contributors
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codecompass-mcp
3
- Version: 3.0.1
3
+ Version: 3.1.0
4
4
  Summary: Structural code context for AI coding agents — a local code knowledge graph for blast radius, impact, deps, dead code, and flow tracing
5
5
  Author: CodeCompass contributors
6
6
  License: MIT
@@ -206,6 +206,7 @@ def _extract_python(root: Node, source: bytes, file_path: str) -> list[CodeTripl
206
206
 
207
207
  exported = _collect_python_exports(root)
208
208
  type_env = _py_type_env(root)
209
+ class_parents = _py_class_parents(root)
209
210
 
210
211
  for node in _walk(root):
211
212
  match node.type:
@@ -298,7 +299,7 @@ def _extract_python(root: Node, source: bytes, file_path: str) -> list[CodeTripl
298
299
  scope = _enclosing_scope(node)
299
300
  caller = scope or module_name
300
301
  caller_type = TYPE_FUNCTION if scope else TYPE_MODULE
301
- receiver_type = _resolve_py_receiver_type(receiver, obj, node, type_env)
302
+ receiver_type = _resolve_py_receiver_type(receiver, obj, node, type_env, class_parents)
302
303
  triples.append(CodeTriple(
303
304
  from_entity=caller,
304
305
  from_type=caller_type,
@@ -447,6 +448,20 @@ def _py_type_env(root: Node) -> dict[str, str]:
447
448
  return types
448
449
 
449
450
 
451
+ def _py_class_parents(root: Node) -> dict[str, str]:
452
+ """Map class name -> its first base class, for resolving `super()`."""
453
+ parents: dict[str, str] = {}
454
+ for node in _walk(root):
455
+ if node.type == "class_definition":
456
+ name = _get_node_name(node, ["identifier"])
457
+ arg = _child_of_type(node, "argument_list")
458
+ if name and arg:
459
+ bases = [c for c in arg.children if c.type == "identifier"]
460
+ if bases:
461
+ parents[name] = _text(bases[0])
462
+ return parents
463
+
464
+
450
465
  def _enclosing_python_class(node: Node) -> str | None:
451
466
  """Name of the nearest enclosing `class C:`, for resolving `self`/`cls`."""
452
467
  current = node.parent
@@ -459,13 +474,15 @@ def _enclosing_python_class(node: Node) -> str | None:
459
474
  return None
460
475
 
461
476
 
462
- def _resolve_py_receiver_type(receiver: str | None, obj: Node | None,
463
- call_node: Node, env: dict[str, str]) -> str | None:
464
- """Infer a Python receiver's type from `self`/`cls`, inline `Foo()`, or env."""
477
+ def _resolve_py_receiver_type(receiver: str | None, obj: Node | None, call_node: Node,
478
+ env: dict[str, str], parents: dict[str, str] | None = None) -> str | None:
479
+ """Infer a Python receiver's type from `self`/`cls`, `super()`, inline `Foo()`, or env."""
465
480
  if _py_call_class(obj) is not None:
466
481
  return _py_call_class(obj)
467
482
  if receiver in ("self", "cls"):
468
483
  return _enclosing_python_class(call_node)
484
+ if receiver in ("super()", "super") and parents:
485
+ return parents.get(_enclosing_python_class(call_node))
469
486
  if receiver is not None and receiver in env:
470
487
  return env[receiver]
471
488
  return None
@@ -726,6 +743,20 @@ def _php_type_env(root: Node) -> dict[str, str]:
726
743
  return types
727
744
 
728
745
 
746
+ def _php_class_parents(root: Node) -> dict[str, str]:
747
+ """Map class name -> its parent (`extends`), for resolving `parent::`."""
748
+ parents: dict[str, str] = {}
749
+ for node in _walk(root):
750
+ if node.type == "class_declaration":
751
+ name = _php_name(node)
752
+ base = _child_of_type(node, "base_clause")
753
+ if name and base:
754
+ targets = _php_extends_targets(base)
755
+ if targets:
756
+ parents[name] = _php_basename(targets[0])
757
+ return parents
758
+
759
+
729
760
  def _php_call_receiver(node: Node, env: dict[str, str]) -> tuple[str | None, str | None]:
730
761
  """Receiver text + inferred type for a member call `$obj->method()`."""
731
762
  obj = node.child_by_field_name("object")
@@ -750,6 +781,7 @@ def _extract_php(root: Node, source: bytes, file_path: str) -> list[CodeTriple]:
750
781
  module_name = _module_name_from_path(file_path)
751
782
  triples: list[CodeTriple] = []
752
783
  type_env = _php_type_env(root)
784
+ class_parents = _php_class_parents(root)
753
785
 
754
786
  def defined_in(name: str, entity_type: str, line: int, is_exported: bool = True,
755
787
  owner: str | None = None) -> None:
@@ -985,7 +1017,19 @@ def _extract_php(root: Node, source: bytes, file_path: str) -> list[CodeTriple]:
985
1017
  case "scoped_call_expression":
986
1018
  names = _children_of_type(node, "name")
987
1019
  if names:
988
- calls(_text(names[-1]), TYPE_FUNCTION, _line(node), node)
1020
+ method = _text(names[-1])
1021
+ scope_node = node.children[0] if node.children else None
1022
+ scope_text = _text(scope_node) if scope_node is not None else ""
1023
+ if scope_text == "parent":
1024
+ container = _enclosing_php_container(node)
1025
+ rtype = class_parents.get(container[0]) if container else None
1026
+ elif scope_text in ("self", "static"):
1027
+ container = _enclosing_php_container(node)
1028
+ rtype = container[0] if container else None
1029
+ else:
1030
+ rtype = _php_basename(scope_node) if scope_node is not None else None
1031
+ calls(method, TYPE_FUNCTION, _line(node), node,
1032
+ receiver=scope_text or None, receiver_type=rtype)
989
1033
 
990
1034
  case "object_creation_expression":
991
1035
  class_node = None
@@ -1014,6 +1058,7 @@ def _extract_javascript(root: Node, source: bytes, file_path: str) -> list[CodeT
1014
1058
 
1015
1059
  # Best-effort variable → type map for receiver type inference on calls.
1016
1060
  type_env = _js_type_env(root)
1061
+ class_parents = _js_class_parents(root)
1017
1062
 
1018
1063
  def defined(name: str, line: int, owner: str | None = None) -> None:
1019
1064
  triples.append(CodeTriple(
@@ -1089,7 +1134,7 @@ def _extract_javascript(root: Node, source: bytes, file_path: str) -> list[CodeT
1089
1134
  scope = _enclosing_js_scope(node)
1090
1135
  caller = scope or module_name
1091
1136
  caller_type = TYPE_FUNCTION if scope else TYPE_MODULE
1092
- receiver_type = _resolve_receiver_type(receiver, obj, node, type_env)
1137
+ receiver_type = _resolve_receiver_type(receiver, obj, node, type_env, class_parents)
1093
1138
  triples.append(CodeTriple(
1094
1139
  from_entity=caller,
1095
1140
  from_type=caller_type,
@@ -1367,13 +1412,33 @@ def _enclosing_class_name(node: Node) -> str | None:
1367
1412
  return None
1368
1413
 
1369
1414
 
1370
- def _resolve_receiver_type(receiver: str | None, obj: Node | None,
1371
- call_node: Node, env: dict[str, str]) -> str | None:
1372
- """Infer the receiver's type from an inline `new`, `this`, or the type env."""
1415
+ def _js_class_parents(root: Node) -> dict[str, str]:
1416
+ """Map class name -> the class it `extends`, for resolving `super`."""
1417
+ parents: dict[str, str] = {}
1418
+ for node in _walk(root):
1419
+ if node.type in ("class_declaration", "class"):
1420
+ name_node = _child_of_type(node, "type_identifier") or _child_of_type(node, "identifier")
1421
+ heritage = _child_of_type(node, "class_heritage")
1422
+ if name_node is not None and heritage is not None:
1423
+ base = None
1424
+ for desc in _walk(heritage):
1425
+ if desc.type in ("identifier", "type_identifier"):
1426
+ base = _text(desc)
1427
+ break
1428
+ if base:
1429
+ parents[_text(name_node)] = base
1430
+ return parents
1431
+
1432
+
1433
+ def _resolve_receiver_type(receiver: str | None, obj: Node | None, call_node: Node,
1434
+ env: dict[str, str], parents: dict[str, str] | None = None) -> str | None:
1435
+ """Infer the receiver's type from an inline `new`, `this`, `super`, or the type env."""
1373
1436
  if _new_expression_type(obj) is not None:
1374
1437
  return _new_expression_type(obj)
1375
1438
  if receiver in ("this",):
1376
1439
  return _enclosing_class_name(call_node)
1440
+ if receiver == "super" and parents:
1441
+ return parents.get(_enclosing_class_name(call_node))
1377
1442
  if receiver is not None and receiver in env:
1378
1443
  return env[receiver]
1379
1444
  return None
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "codecompass-mcp"
3
- version = "3.0.1"
3
+ version = "3.1.0"
4
4
  description = "Structural code context for AI coding agents — a local code knowledge graph for blast radius, impact, deps, dead code, and flow tracing"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}
@@ -339,6 +339,21 @@ def test_python_public_method_marked_exported():
339
339
  assert by_name.get("_private") is False, "underscore method is not exported"
340
340
 
341
341
 
342
+ def test_inheritance_super_resolution_all_langs():
343
+ cases = [
344
+ (".py", "class Command:\n def invoke(self, c): pass\n"
345
+ "class Group(Command):\n def invoke(self, c):\n return super().invoke(c)\n"),
346
+ (".js", "class Command { invoke(c){} }\n"
347
+ "class Group extends Command { invoke(c){ return super.invoke(c); } }\n"),
348
+ (".php", "<?php\nclass Command { function invoke($c){} }\n"
349
+ "class Group extends Command { function invoke($c){ return parent::invoke($c); } }\n"),
350
+ ]
351
+ for suffix, src in cases:
352
+ triples = _parse(src, suffix=suffix)
353
+ call = next(t for t in triples if t.relation_type == "CALLS" and t.to_entity == "invoke")
354
+ assert call.call_receiver_type == "Command", f"{suffix}: super()/parent:: should type as the parent class"
355
+
356
+
342
357
  def test_php_receiver_type_and_exports():
343
358
  src = (
344
359
  "<?php\n"
File without changes
File without changes