codegraphcontext 0.5.6__py3-none-any.whl → 0.5.7__py3-none-any.whl

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.
@@ -2830,15 +2830,18 @@ def analyze_complexity(
2830
2830
 
2831
2831
  @analyze_app.command("dead-code")
2832
2832
  def analyze_dead_code(
2833
- path: Optional[str] = typer.Argument(None, help="Path to analyze (not yet implemented)"),
2833
+ path: Optional[str] = typer.Argument(None, help="Repository path to scope the analysis to"),
2834
2834
  exclude_decorators: Optional[str] = typer.Option(None, "--exclude", "-e", help="Comma-separated decorators to exclude"),
2835
2835
  context: Optional[str] = typer.Option(None, "--context", "-c", help="Specific context to use"),
2836
2836
  ):
2837
2837
  """
2838
- Find potentially unused functions and classes.
2839
-
2838
+ Find potentially unused functions.
2839
+
2840
+ Without a path the whole database is scanned, which on a shared graph
2841
+ reports dead code from every indexed repository. Pass a path to scope it.
2842
+
2840
2843
  Example:
2841
- cgc analyze dead-code
2844
+ cgc analyze dead-code .
2842
2845
  cgc analyze dead-code --exclude route,task,api
2843
2846
  """
2844
2847
  _load_credentials()
@@ -2849,8 +2852,11 @@ def analyze_dead_code(
2849
2852
 
2850
2853
  try:
2851
2854
  exclude_list = exclude_decorators.split(',') if exclude_decorators else []
2852
- results = code_finder.find_dead_code(exclude_list)
2853
-
2855
+ # `path` was accepted and then dropped, so running inside one repository
2856
+ # still reported dead code from every repository in the database.
2857
+ repo_path = Path(path).resolve().as_posix() if path else None
2858
+ results = code_finder.find_dead_code(exclude_list, repo_path=repo_path)
2859
+
2854
2860
  unused_funcs = results.get('potentially_unused_functions', [])
2855
2861
 
2856
2862
  if not unused_funcs:
@@ -14,6 +14,22 @@ logger = logging.getLogger(__name__)
14
14
 
15
15
  _MAX_TRAVERSAL_DEPTH = 20
16
16
 
17
+ # Names that are entry points rather than dead code. Matched on the *whole*
18
+ # name, lowercased. This used to be a substring test (`name CONTAINS 'main'`,
19
+ # `toLower(name) CONTAINS 'entry'`), which silently excluded any function whose
20
+ # name merely contained one of them — `domain_check`, `remainder`,
21
+ # `maintain_index`, `entry_count` — so genuinely unused code was never
22
+ # reported and there was no way to tell.
23
+ _ENTRY_POINT_NAMES = (
24
+ "main",
25
+ "setup",
26
+ "run",
27
+ "application",
28
+ "entry",
29
+ "entrypoint",
30
+ )
31
+ _ENTRY_POINT_NAMES_CYPHER = "[" + ", ".join(f"'{n}'" for n in _ENTRY_POINT_NAMES) + "]"
32
+
17
33
 
18
34
  def _sanitize_depth(depth, default: int = 3) -> int:
19
35
  """Coerce and clamp a traversal depth before interpolating it into Cypher.
@@ -809,19 +825,16 @@ class CodeFinder:
809
825
  query = f"""
810
826
  MATCH (func:Function)
811
827
  WHERE func.is_dependency = false {repo_filter} {func_ignore}
812
- AND NOT func.name IN ['main', 'setup', 'run']
828
+ AND NOT toLower(func.name) IN {_ENTRY_POINT_NAMES_CYPHER}
813
829
  AND func.name <> '<module>'
814
830
  AND NOT (func.name STARTS WITH '__' AND func.name ENDS WITH '__')
815
831
  AND NOT func.name STARTS WITH '_test'
816
832
  AND NOT func.name STARTS WITH 'test_'
817
- AND NOT func.name CONTAINS 'main'
818
- AND NOT toLower(func.name) CONTAINS 'application'
819
- AND NOT toLower(func.name) CONTAINS 'entry'
820
- AND NOT toLower(func.name) CONTAINS 'entrypoint'
821
833
  {decorator_filter}
822
834
  WITH func
823
- OPTIONAL MATCH (caller:Function)-[:CALLS|HEURISTIC_CALLS]->(func)
824
- WHERE caller.is_dependency = false {caller_ignore}
835
+ OPTIONAL MATCH (caller)-[:CALLS|HEURISTIC_CALLS]->(func)
836
+ WHERE (caller.is_dependency IS NULL OR caller.is_dependency = false)
837
+ {caller_ignore}
825
838
  WITH func, count(caller) as caller_count
826
839
  WHERE caller_count = 0
827
840
  OPTIONAL MATCH (file:File)-[:CONTAINS]->(func)
@@ -1211,16 +1224,31 @@ class CodeFinder:
1211
1224
  try:
1212
1225
  if query_type == "find_callers":
1213
1226
  results = self.who_calls_function(target, context, repo_path=repo_path)
1214
- # Hoist target_file_path (constant across rows) to the envelope once,
1215
- # instead of repeating the absolute path in every row (payload slimming).
1216
- target_file_path = next((r.pop("target_file_path", None) for r in results), None) if results else None
1217
- for r in results:
1218
- r.pop("target_file_path", None)
1219
- return {
1227
+ # target_file_path is only constant when `context` pinned a single
1228
+ # definition. Without it, who_calls_function matches
1229
+ # Function {name: $function_name} across every file, so rows
1230
+ # legitimately carry different targets — hoisting the first row's
1231
+ # value and stripping the rest reported every caller as calling
1232
+ # whichever definition happened to sort first.
1233
+ #
1234
+ # Hoist only when the rows agree; otherwise leave the field on each
1235
+ # row so the ambiguity is visible to the caller.
1236
+ target_paths = {r.get("target_file_path") for r in results}
1237
+ target_file_path = target_paths.pop() if len(target_paths) == 1 else None
1238
+ if target_file_path is not None:
1239
+ for r in results:
1240
+ r.pop("target_file_path", None)
1241
+ envelope = {
1220
1242
  "query_type": "find_callers", "target": target, "context": context,
1221
1243
  "target_file_path": target_file_path, "results": results,
1222
1244
  "summary": f"Found {len(results)} functions that call '{target}'"
1223
1245
  }
1246
+ if target_file_path is None and len(target_paths) > 1:
1247
+ envelope["note"] = (
1248
+ f"'{target}' is defined in {len(target_paths)} files; each result "
1249
+ "carries its own target_file_path. Pass `context` to disambiguate."
1250
+ )
1251
+ return envelope
1224
1252
 
1225
1253
  elif query_type == "find_callees":
1226
1254
  results = self.what_does_function_call(target, context, repo_path=repo_path)
@@ -1272,7 +1300,10 @@ class CodeFinder:
1272
1300
  }
1273
1301
 
1274
1302
  elif query_type in ["dead_code", "unused", "unreachable"]:
1275
- results = self.find_dead_code(repo_path=repo_path)
1303
+ # graph_name must be forwarded: find_dead_code defaults it to None and
1304
+ # re-assigns the shared self._active_graph, so omitting it here
1305
+ # silently redirected the query to the default graph.
1306
+ results = self.find_dead_code(repo_path=repo_path, graph_name=graph_name)
1276
1307
  return {
1277
1308
  "query_type": "dead_code", "results": results,
1278
1309
  "summary": f"Found {len(results['potentially_unused_functions'])} potentially unused functions"
@@ -1281,7 +1312,7 @@ class CodeFinder:
1281
1312
  elif query_type == "find_complexity":
1282
1313
  limit_val = context if context else target
1283
1314
  limit = int(limit_val) if limit_val and str(limit_val).isdigit() else 10
1284
- results = self.find_most_complex_functions(limit, repo_path=repo_path)
1315
+ results = self.find_most_complex_functions(limit, repo_path=repo_path, graph_name=graph_name)
1285
1316
  return {
1286
1317
  "query_type": "find_complexity", "limit": limit, "results": results,
1287
1318
  "summary": f"Found the top {len(results)} most complex functions"
@@ -236,8 +236,8 @@ def find_java_spring_beans(code_finder: CodeFinder, **args) -> Dict[str, Any]:
236
236
  query = f"""
237
237
  MATCH (c:Class)
238
238
  WHERE {where_clause}
239
- OPTIONAL MATCH ()-[:INJECTS]->(c)
240
- WITH c, count(*) AS injection_count
239
+ OPTIONAL MATCH ()-[inj:INJECTS]->(c)
240
+ WITH c, count(inj) AS injection_count
241
241
  RETURN c.name AS name, c.spring_stereotype AS stereotype,
242
242
  c.path AS file, c.line_number AS line_number, injection_count
243
243
  ORDER BY stereotype, name
@@ -296,7 +296,8 @@ def find_datasource_nodes(code_finder: CodeFinder, **args) -> Dict[str, Any]:
296
296
  {kp_where}
297
297
  OPTIONAL MATCH (kp:RedisKeyPattern)-[:STORED_IN]->(d)
298
298
  RETURN d.name AS datasource,
299
- collect({{pattern: kp.pattern, key_type: kp.key_type, count: kp.count}}) AS key_patterns
299
+ [x IN collect(kp) | {{pattern: x.pattern, key_type: x.key_type,
300
+ count: x.count}}] AS key_patterns
300
301
  """
301
302
 
302
303
  try:
@@ -19,6 +19,25 @@ def add_code_to_graph(graph_builder, job_manager, loop, list_repos_func, **args)
19
19
 
20
20
  if not path:
21
21
  return {"error": "Path is a required argument (repo_path)."}
22
+
23
+ # `graph_name` is advertised in this tool's schema but indexing cannot yet
24
+ # honour it: GraphBuilder binds its GraphWriter to the default driver at
25
+ # construction, so a named graph would need a per-job writer threaded
26
+ # through the whole pipeline. Refuse explicitly rather than write to the
27
+ # default graph and report success — an agent that believed the repository
28
+ # landed in a named graph would find nothing there later, with no error to
29
+ # explain it.
30
+ graph_name = args.get("graph_name")
31
+ if graph_name:
32
+ return {
33
+ "error": (
34
+ f"Indexing into a named graph is not supported yet, so "
35
+ f"graph_name={graph_name!r} cannot be honoured. Omit it to index "
36
+ "into the default graph, or select the graph via the CLI context "
37
+ "(`cgc context create` / `cgc index --context`)."
38
+ ),
39
+ "unsupported_argument": "graph_name",
40
+ }
22
41
 
23
42
  try:
24
43
  path_obj = Path(path).resolve()
@@ -354,15 +354,15 @@ def get_repository_stats(code_finder: CodeFinder, **args) -> Dict[str, Any]:
354
354
  }
355
355
 
356
356
  # 1. Files
357
- file_query = "MATCH (r:Repository {path: $path})-[:CONTAINS*]->(f:File) RETURN count(f) as c"
357
+ file_query = "MATCH (r:Repository {path: $path})-[:CONTAINS*]->(f:File) RETURN count(DISTINCT f) as c"
358
358
  file_count = session.run(file_query, path=repo_path_obj).single()["c"]
359
359
 
360
360
  # 2. Functions
361
- func_query = "MATCH (r:Repository {path: $path})-[:CONTAINS*]->(func:Function) RETURN count(func) as c"
361
+ func_query = "MATCH (r:Repository {path: $path})-[:CONTAINS*]->(func:Function) RETURN count(DISTINCT func) as c"
362
362
  func_count = session.run(func_query, path=repo_path_obj).single()["c"]
363
363
 
364
364
  # 3. Classes
365
- class_query = "MATCH (r:Repository {path: $path})-[:CONTAINS*]->(cls:Class) RETURN count(cls) as c"
365
+ class_query = "MATCH (r:Repository {path: $path})-[:CONTAINS*]->(cls:Class) RETURN count(DISTINCT cls) as c"
366
366
  class_count = session.run(class_query, path=repo_path_obj).single()["c"]
367
367
 
368
368
  # 4. Modules (imported)
@@ -191,6 +191,14 @@ async def run_scip_index_async(
191
191
  cgcignore_path=cgcignore_path,
192
192
  supported_extensions=set(parsers_keys),
193
193
  )
194
+ # Recompute total_files now that we know the full set (SCIP-covered +
195
+ # supplementary), so progress_percentage stays <= 100% throughout.
196
+ if job_id:
197
+ new_total = len(files_data) + sum(
198
+ 1 for f in supplementary_files
199
+ if str(f.resolve()) not in scip_abs_paths
200
+ )
201
+ job_manager.update_job(job_id, total_files=new_total)
194
202
  minimal_nodes = 0
195
203
  for repo_file in supplementary_files:
196
204
  abs_str = str(repo_file.resolve())
@@ -6,20 +6,37 @@ from codegraphcontext.utils.debug_log import debug_log, info_logger, error_logge
6
6
  from codegraphcontext.utils.tree_sitter_manager import execute_query
7
7
 
8
8
  C_QUERIES = {
9
+ # A pointer return type wraps the function declarator from the OUTSIDE:
10
+ # `char* f()` is
11
+ # function_definition > pointer_declarator > function_declarator > identifier
12
+ # The second alternative below used to nest these the other way round, which
13
+ # matches nothing, so no function returning `T*` or `T**` was extracted —
14
+ # and because pre_scan_c shares this query they were invisible as call
15
+ # targets too.
9
16
  "functions": """
10
17
  (function_definition
11
18
  declarator: (function_declarator
12
19
  declarator: (identifier) @name
13
20
  )
14
21
  ) @function_node
15
-
22
+
16
23
  (function_definition
17
- declarator: (function_declarator
18
- declarator: (pointer_declarator
24
+ declarator: (pointer_declarator
25
+ declarator: (function_declarator
19
26
  declarator: (identifier) @name
20
27
  )
21
28
  )
22
29
  ) @function_node
30
+
31
+ (function_definition
32
+ declarator: (pointer_declarator
33
+ declarator: (pointer_declarator
34
+ declarator: (function_declarator
35
+ declarator: (identifier) @name
36
+ )
37
+ )
38
+ )
39
+ ) @function_node
23
40
  """,
24
41
  "structs": """
25
42
  (struct_specifier
@@ -762,15 +779,25 @@ def pre_scan_c(files: list[Path], parser_wrapper) -> dict:
762
779
  declarator: (identifier) @name
763
780
  )
764
781
  )
765
-
782
+
766
783
  (function_definition
767
- declarator: (function_declarator
768
- declarator: (pointer_declarator
784
+ declarator: (pointer_declarator
785
+ declarator: (function_declarator
769
786
  declarator: (identifier) @name
770
787
  )
771
788
  )
772
789
  )
773
-
790
+
791
+ (function_definition
792
+ declarator: (pointer_declarator
793
+ declarator: (pointer_declarator
794
+ declarator: (function_declarator
795
+ declarator: (identifier) @name
796
+ )
797
+ )
798
+ )
799
+ )
800
+
774
801
  (struct_specifier
775
802
  name: (type_identifier) @name
776
803
  )
@@ -6,6 +6,13 @@ from codegraphcontext.utils.debug_log import debug_log, info_logger, error_logge
6
6
  from codegraphcontext.utils.tree_sitter_manager import execute_query
7
7
 
8
8
  CPP_QUERIES = {
9
+ # A pointer or reference return type wraps the function declarator from the
10
+ # OUTSIDE: `char* f()` is
11
+ # function_definition > pointer_declarator > function_declarator > identifier
12
+ # not function_declarator > pointer_declarator. Matching only the unwrapped
13
+ # shape meant no function returning `T*`, `T&` or `T**` was ever extracted —
14
+ # accessors, factories and `operator[]` are pervasive in C++, and because
15
+ # pre_scan_cpp shares this query they were invisible as call targets too.
9
16
  "functions": """
10
17
  (function_definition
11
18
  declarator: (function_declarator
@@ -16,6 +23,44 @@ CPP_QUERIES = {
16
23
  ]
17
24
  )
18
25
  ) @function_node
26
+
27
+ (function_definition
28
+ declarator: (pointer_declarator
29
+ declarator: (function_declarator
30
+ declarator: [
31
+ (identifier) @name
32
+ (field_identifier) @name
33
+ (qualified_identifier) @qualified_name
34
+ ]
35
+ )
36
+ )
37
+ ) @function_node
38
+
39
+ (function_definition
40
+ declarator: (pointer_declarator
41
+ declarator: (pointer_declarator
42
+ declarator: (function_declarator
43
+ declarator: [
44
+ (identifier) @name
45
+ (field_identifier) @name
46
+ (qualified_identifier) @qualified_name
47
+ ]
48
+ )
49
+ )
50
+ )
51
+ ) @function_node
52
+
53
+ (function_definition
54
+ declarator: (reference_declarator
55
+ (function_declarator
56
+ declarator: [
57
+ (identifier) @name
58
+ (field_identifier) @name
59
+ (qualified_identifier) @qualified_name
60
+ ]
61
+ )
62
+ )
63
+ ) @function_node
19
64
  """,
20
65
  "classes": """
21
66
  (class_specifier
@@ -651,6 +696,23 @@ def pre_scan_cpp(files: list[Path], parser_wrapper) -> dict:
651
696
  (type_definition declarator: (type_identifier) @name)
652
697
  (function_definition declarator: (function_declarator declarator: (identifier) @name))
653
698
  (function_definition declarator: (function_declarator declarator: (qualified_identifier) @qualified_name))
699
+
700
+ ; A pointer/reference return type wraps the function declarator from the
701
+ ; outside, so these forms need their own alternatives — without them a
702
+ ; `T* f()` was not registered here either, and so was unresolvable as a
703
+ ; call target even once it became a node.
704
+ (function_definition declarator: (pointer_declarator
705
+ declarator: (function_declarator declarator: (identifier) @name)))
706
+ (function_definition declarator: (pointer_declarator
707
+ declarator: (function_declarator declarator: (qualified_identifier) @qualified_name)))
708
+ (function_definition declarator: (pointer_declarator declarator: (pointer_declarator
709
+ declarator: (function_declarator declarator: (identifier) @name))))
710
+ (function_definition declarator: (pointer_declarator declarator: (pointer_declarator
711
+ declarator: (function_declarator declarator: (qualified_identifier) @qualified_name))))
712
+ (function_definition declarator: (reference_declarator
713
+ (function_declarator declarator: (identifier) @name)))
714
+ (function_definition declarator: (reference_declarator
715
+ (function_declarator declarator: (qualified_identifier) @qualified_name)))
654
716
  """
655
717
 
656
718
 
@@ -6,6 +6,21 @@ from codegraphcontext.utils.debug_log import debug_log, info_logger, error_logge
6
6
  from codegraphcontext.utils.tree_sitter_manager import execute_query
7
7
 
8
8
  CSHARP_QUERIES = {
9
+ # Fields and locals share a shape: `variable_declaration` carries the type
10
+ # and holds one `variable_declarator` per name. There was no `variables`
11
+ # query at all and the key was hardcoded to [], so C# produced no Variable
12
+ # nodes — and `resolution/calls.py` lost the local-variable declarations it
13
+ # uses to infer a receiver's type, so `var s = new UserService(); s.Save();`
14
+ # could not resolve Save to UserService.Save.
15
+ "variables": """
16
+ (field_declaration
17
+ (variable_declaration) @decl
18
+ ) @field_node
19
+
20
+ (local_declaration_statement
21
+ (variable_declaration) @decl
22
+ ) @local_node
23
+ """,
9
24
  "functions": """
10
25
  (method_declaration
11
26
  name: (identifier) @name
@@ -123,6 +138,7 @@ class CSharpTreeSitterParser:
123
138
  parsed_enums = []
124
139
  parsed_records = []
125
140
  parsed_properties = []
141
+ parsed_variables = []
126
142
  parsed_imports = []
127
143
  parsed_calls = []
128
144
 
@@ -143,6 +159,8 @@ class CSharpTreeSitterParser:
143
159
  parsed_records = self._parse_type_declarations(captures, source_code, path, "Record")
144
160
  elif capture_name == "properties":
145
161
  parsed_properties = self._parse_properties(captures, source_code, path, tree.root_node)
162
+ elif capture_name == "variables":
163
+ parsed_variables = self._parse_variables(captures)
146
164
  elif capture_name == "imports":
147
165
  parsed_imports = self._parse_imports(captures, source_code)
148
166
  elif capture_name == "calls":
@@ -157,7 +175,7 @@ class CSharpTreeSitterParser:
157
175
  "enums": parsed_enums,
158
176
  "records": parsed_records,
159
177
  "properties": parsed_properties,
160
- "variables": [],
178
+ "variables": parsed_variables,
161
179
  "imports": parsed_imports,
162
180
  "function_calls": parsed_calls,
163
181
  "is_dependency": is_dependency,
@@ -359,6 +377,70 @@ class CSharpTreeSitterParser:
359
377
 
360
378
  return imports
361
379
 
380
+ def _parse_variables(self, captures: list) -> list:
381
+ """Fields and locals, from `variable_declaration` nodes.
382
+
383
+ Both shapes nest identically — `variable_declaration` carries the type
384
+ and holds one `variable_declarator` per declared name, so
385
+ `int a = 1, b = 2;` yields two rows sharing a type.
386
+ """
387
+ variables = []
388
+ seen = set()
389
+ for node, capture_name in captures:
390
+ if capture_name != "decl":
391
+ continue
392
+ type_node = node.child_by_field_name("type")
393
+ var_type = self._get_node_text(type_node) if type_node is not None else None
394
+
395
+ for declarator in node.children:
396
+ if declarator.type != "variable_declarator":
397
+ continue
398
+ name_node = declarator.child_by_field_name("name")
399
+ if name_node is None:
400
+ name_node = next(
401
+ (c for c in declarator.children if c.type == "identifier"), None
402
+ )
403
+ if name_node is None:
404
+ continue
405
+
406
+ line_number = name_node.start_point[0] + 1
407
+ name = self._get_node_text(name_node)
408
+ if (name, line_number) in seen:
409
+ continue
410
+ seen.add((name, line_number))
411
+
412
+ # tree-sitter-c-sharp has no `equals_value_clause`: the
413
+ # initialiser is a direct sibling following the `=` token.
414
+ value = None
415
+ seen_equals = False
416
+ for child in declarator.children:
417
+ if child.type == "=":
418
+ seen_equals = True
419
+ continue
420
+ if seen_equals and child.is_named:
421
+ value = self._get_node_text(child)
422
+ break
423
+
424
+ context, _ctx_type, _ctx_line = self._get_parent_context(
425
+ declarator, types=("method_declaration", "function_declaration")
426
+ )
427
+ class_context, _c_type, _c_line = self._get_parent_context(
428
+ declarator,
429
+ types=("class_declaration", "struct_declaration", "record_declaration"),
430
+ )
431
+
432
+ variables.append({
433
+ "name": name,
434
+ "line_number": line_number,
435
+ "value": value,
436
+ "type": var_type,
437
+ "context": context,
438
+ "class_context": class_context,
439
+ "lang": self.language_name,
440
+ "is_dependency": False,
441
+ })
442
+ return variables
443
+
362
444
  def _get_parent_context(self, node: Any, types: Tuple[str, ...] = ('class_declaration', 'struct_declaration', 'function_declaration', 'method_declaration')):
363
445
  """Find parent context for C# constructs."""
364
446
  curr = node.parent
@@ -360,13 +360,77 @@ class JavascriptTreeSitterParser:
360
360
  left_child = child.child_by_field_name('left')
361
361
  if left_child and left_child.type == 'identifier':
362
362
  params.append(self._get_node_text(left_child))
363
- elif child.type == 'rest_pattern':
363
+ elif child.type in ('rest_element', 'rest_pattern'):
364
364
  # Rest parameter: ...args
365
- argument = child.child_by_field_name('argument')
366
- if argument and argument.type == 'identifier':
367
- params.append(f"...{self._get_node_text(argument)}")
365
+ # Try named field first, then direct identifier child
366
+ name_node = (child.child_by_field_name('name') or
367
+ child.child_by_field_name('argument'))
368
+ if name_node is None:
369
+ name_node = next(
370
+ (c for c in child.children if c.type == 'identifier'), None
371
+ )
372
+ if name_node:
373
+ params.append(f"...{self._get_node_text(name_node)}")
374
+ elif child.type == 'object_pattern':
375
+ # Destructured object: {a}, {a: renamed}, {a = 1}
376
+ names = self._pattern_binding_names(child)
377
+ params.append("{" + ", ".join(names) + "}" if names else "{...}")
378
+ elif child.type == 'array_pattern':
379
+ # Destructured array: [a, b]
380
+ names = self._pattern_binding_names(child)
381
+ params.append("[" + ", ".join(names) + "]" if names else "[...]")
368
382
  return params
369
383
 
384
+ def _pattern_binding_names(self, pattern_node):
385
+ """Local names a destructuring pattern binds, in source order.
386
+
387
+ Recorded rather than collapsed to a placeholder because the binding is
388
+ what later code refers to — `function Button({label})` is the dominant
389
+ React idiom, and `label` is the name that appears in the body. One
390
+ entry is still emitted per *parameter position* so arity, which call
391
+ resolution matches on, is unchanged.
392
+ """
393
+ names = []
394
+
395
+ def walk(node):
396
+ for child in node.named_children:
397
+ if child.type == 'shorthand_property_identifier_pattern':
398
+ # { a }
399
+ names.append(self._get_node_text(child))
400
+ elif child.type == 'pair_pattern':
401
+ # { a: local } — the binding is the value, not the key
402
+ value = child.child_by_field_name('value')
403
+ if value is not None and value.type == 'identifier':
404
+ names.append(self._get_node_text(value))
405
+ elif value is not None:
406
+ walk(value) # nested destructuring
407
+ elif child.type == 'object_assignment_pattern':
408
+ # { a = 1 }
409
+ target = child.child_by_field_name('left') or (
410
+ child.named_children[0] if child.named_children else None
411
+ )
412
+ if target is not None:
413
+ if target.type in (
414
+ 'identifier', 'shorthand_property_identifier_pattern'
415
+ ):
416
+ names.append(self._get_node_text(target))
417
+ else:
418
+ walk(target)
419
+ elif child.type == 'identifier':
420
+ # [ a, b ]
421
+ names.append(self._get_node_text(child))
422
+ elif child.type in ('object_pattern', 'array_pattern'):
423
+ walk(child)
424
+ elif child.type in ('rest_element', 'rest_pattern'):
425
+ inner = next(
426
+ (c for c in child.children if c.type == 'identifier'), None
427
+ )
428
+ if inner is not None:
429
+ names.append(f"...{self._get_node_text(inner)}")
430
+
431
+ walk(pattern_node)
432
+ return names
433
+
370
434
 
371
435
  def _get_jsdoc_comment(self, func_node):
372
436
  """Extract JSDoc comment preceding the function."""
@@ -180,14 +180,47 @@ class RubyTreeSitterParser:
180
180
  prev_sibling = prev_sibling.prev_sibling
181
181
  return None
182
182
 
183
+ # Ruby parameter node types, and the prefix each carries in source. The
184
+ # bare `identifier` case is a required positional parameter.
185
+ _PARAM_PREFIXES = {
186
+ 'optional_parameter': '', # b = 1
187
+ 'keyword_parameter': '', # key:
188
+ 'splat_parameter': '*', # *rest
189
+ 'hash_splat_parameter': '**', # **opts
190
+ 'block_parameter': '&', # &blk
191
+ }
192
+
183
193
  def _parse_method_parameters(self, method_node: Any) -> list[str]:
184
- """Parse method parameters from a method node."""
194
+ """Parse method parameters from a method node.
195
+
196
+ Parameters live inside a `method_parameters` child, not directly on the
197
+ method — the only direct `identifier` child is the method's own name,
198
+ which this used to skip, so every Ruby method reported no parameters at
199
+ all and no `HAS_PARAMETER` edge was ever created.
200
+ """
201
+ params_node = method_node.child_by_field_name('parameters')
202
+ if params_node is None:
203
+ params_node = next(
204
+ (c for c in method_node.children
205
+ if c.type in ('method_parameters', 'parameters', 'bare_parameters')),
206
+ None,
207
+ )
208
+ if params_node is None:
209
+ return []
210
+
185
211
  params = []
186
- # Look for parameters in the method node
187
- for child in method_node.children:
188
- if child.type == 'identifier' and child != method_node.child_by_field_name('name'):
189
- # This is likely a parameter
212
+ for child in params_node.children:
213
+ if not child.is_named:
214
+ continue # punctuation: ( ) ,
215
+ if child.type == 'identifier':
190
216
  params.append(self._get_node_text(child))
217
+ continue
218
+ prefix = self._PARAM_PREFIXES.get(child.type)
219
+ if prefix is None:
220
+ continue
221
+ name_node = next((c for c in child.children if c.type == 'identifier'), None)
222
+ if name_node is not None:
223
+ params.append(prefix + self._get_node_text(name_node))
191
224
  return params
192
225
 
193
226
  def parse(self, path: Path, is_dependency: bool = False, index_source: bool = False) -> Dict[str, Any]:
@@ -297,23 +330,26 @@ class RubyTreeSitterParser:
297
330
  # Collect all captures first
298
331
  all_captures = list(execute_query(self.language, query_str, root_node))
299
332
 
300
- # Group captures by class node using a different approach
333
+ # Read each class's own `name` field rather than matching @name captures
334
+ # to classes by byte range. An outer class always contains an inner
335
+ # class's name node, so range-matching assigned the inner name to the
336
+ # outer class (the first container, in document order) and left the
337
+ # inner entry nameless — where it was then dropped. `class Client`
338
+ # wrapping `class TimeoutError` yielded exactly one class, named
339
+ # TimeoutError, carrying Client's line range.
301
340
  captures_by_class = {}
302
341
  for node, capture_name in all_captures:
303
- if capture_name == 'class':
304
- captures_by_class[id(node)] = {'node': node, 'name': None}
305
-
306
- # Now find names for each class
307
- for node, capture_name in all_captures:
308
- if capture_name == 'name':
309
- # Find which class this name belongs to
310
- for class_id, class_data in captures_by_class.items():
311
- class_node = class_data['node']
312
- # Check if this name node is within the class node
313
- if (node.start_byte >= class_node.start_byte and
314
- node.end_byte <= class_node.end_byte):
315
- captures_by_class[class_id]['name'] = self._get_node_text(node)
316
- break
342
+ if capture_name != 'class':
343
+ continue
344
+ name_node = node.child_by_field_name('name')
345
+ if name_node is None:
346
+ # tree-sitter-ruby gives the `class` keyword token the same
347
+ # node type as the definition; only the definition has a name.
348
+ continue
349
+ captures_by_class[id(node)] = {
350
+ 'node': node,
351
+ 'name': self._get_node_text(name_node),
352
+ }
317
353
 
318
354
  # Build class entries
319
355
  for class_data in captures_by_class.values():
@@ -49,7 +49,15 @@ def _get_python_package_path(package_name: str) -> Optional[str]:
49
49
  if _is_stdlib_module(package_name):
50
50
  return str(module_file)
51
51
 
52
- return str(module_file.parent)
52
+ # For a flat, single-file package (e.g. six.py, typing_extensions.py),
53
+ # the parent is site-packages itself. Returning it would cause
54
+ # add_package_to_graph to index every installed dependency.
55
+ # Return the file instead so only the package is indexed.
56
+ parent = module_file.parent
57
+ if parent.name in ("site-packages", "dist-packages"):
58
+ return str(module_file)
59
+
60
+ return str(parent)
53
61
 
54
62
  if spec.submodule_search_locations:
55
63
  locations = list(spec.submodule_search_locations)
@@ -114,8 +114,8 @@ class JavaToolkit:
114
114
  f"""
115
115
  MATCH (c:Class)
116
116
  WHERE {where}
117
- OPTIONAL MATCH ()-[:INJECTS]->(c)
118
- WITH c, count(*) AS injection_count
117
+ OPTIONAL MATCH ()-[inj:INJECTS]->(c)
118
+ WITH c, count(inj) AS injection_count
119
119
  RETURN c.name AS name, c.spring_stereotype AS stereotype,
120
120
  c.path AS file, c.line_number AS line_number,
121
121
  injection_count
@@ -220,12 +220,25 @@ def _section_cross_module_calls(driver: Any, limit: int = 20, repo_path: Optiona
220
220
 
221
221
  def _section_dead_code(driver: Any, limit: int = 20, repo_path: Optional[str] = None) -> str:
222
222
  """Functions with no incoming CALLS edges (potential dead code)."""
223
+ # Share the entry-point list with `CodeFinder.find_dead_code` so the report
224
+ # and the CLI/MCP tool cannot disagree. They previously did, in both
225
+ # directions: this query had no name exclusions at all (so every dunder and
226
+ # `test_*` showed up as dead), while `find_dead_code` restricted callers to
227
+ # `(caller:Function)` and so missed the File-sourced edges that this one
228
+ # correctly counts.
229
+ from .code_finder import _ENTRY_POINT_NAMES_CYPHER
230
+
223
231
  rows = _run_cypher(
224
232
  driver,
225
- """
233
+ f"""
226
234
  MATCH (fn:Function)
227
235
  WHERE (fn.is_dependency IS NULL OR fn.is_dependency = false)
228
236
  AND ($repo_path IS NULL OR fn.path STARTS WITH $repo_path)
237
+ AND NOT toLower(fn.name) IN {_ENTRY_POINT_NAMES_CYPHER}
238
+ AND fn.name <> '<module>'
239
+ AND NOT (fn.name STARTS WITH '__' AND fn.name ENDS WITH '__')
240
+ AND NOT fn.name STARTS WITH '_test'
241
+ AND NOT fn.name STARTS WITH 'test_'
229
242
  AND NOT ()-[:CALLS|HEURISTIC_CALLS]->(fn)
230
243
  RETURN fn.name AS name, fn.path AS path
231
244
  ORDER BY fn.path, fn.name
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codegraphcontext
3
- Version: 0.5.6
3
+ Version: 0.5.7
4
4
  Summary: An MCP server that indexes local code into a graph database to provide context to AI assistants.
5
5
  Author-email: Shashank Shekhar Singh <shashankshekharsingh1205@gmail.com>
6
6
  License: MIT License
@@ -372,8 +372,8 @@ CodeGraphContext supports multiple graph database backends to suit your environm
372
372
  | **Platform** | **All (Windows Native, macOS, Linux)** | **All (Windows Native, macOS, Linux)** | Unix-only (Linux/macOS/WSL) | All Platforms |
373
373
  | **Use Case** | Desktop, IDE, Local development | Custom research projects | Specialized Unix development | Enterprise, Massive graphs |
374
374
  | **Requirement**| `pip install kuzu` | `pip install ladybug` | `pip install falkordblite` | Neo4j Server / Docker / Nornic Cloud |
375
- | **Speed** | ⚡ Extremely Fast | ⚡ Fast | 🚀 Scalable |
376
- | **Persistence**| Yes (to disk) | Yes (to disk) | Yes (to disk) |
375
+ | **Speed** | ⚡ Extremely Fast | ⚡ Fast | 🚀 Scalable | 🌐 Network-dependent |
376
+ | **Persistence**| Yes (to disk) | Yes (to disk) | Yes (to disk) | Yes (server-side) |
377
377
 
378
378
  ---
379
379
 
@@ -14,7 +14,7 @@ codegraphcontext/cli/__init__.py,sha256=2vUlnf7H09M9JbS2gS_LmMUBJWAWcGGHkkl4I2Yr
14
14
  codegraphcontext/cli/cli_helpers.py,sha256=w7TCwcL5T6XyRPYKzLkBdt0V6O3YEkWlPl_WTBZHWEA,54179
15
15
  codegraphcontext/cli/config_manager.py,sha256=wjYT-aYreRfgRxd0Hy_thLSmlgsV2_JRuMVlVlZlEBo,54960
16
16
  codegraphcontext/cli/hook_manager.py,sha256=nKr6SiuM6zA3eZv1rZSNPXUyqkha8Ja0VGQ3_145bLE,7615
17
- codegraphcontext/cli/main.py,sha256=9JL3Y7rA3LnUweRcRKV7HMQmT5FLSBYjOI1qjyvSIuo,140755
17
+ codegraphcontext/cli/main.py,sha256=G5DEaVvLX4rqOrB2YemRz8YvG8Alc0Naot9B_rhwTJ0,141135
18
18
  codegraphcontext/cli/project_config.py,sha256=Slae6fIojlcqRbFzRkAMAsglamryLCPrjUFG16e165g,7492
19
19
  codegraphcontext/cli/registry_commands.py,sha256=KwLwxKzMWs0tAFd__nPf_gz3kmlPO5b6Zcb_9Qkguf0,18516
20
20
  codegraphcontext/cli/setup_macos.py,sha256=0jcxRsQX7_UcfKrFUnc5Yx3m34w0C3w3x5n7KH6ty6g,3743
@@ -39,10 +39,10 @@ codegraphcontext/core/simulator.py,sha256=Sb3PpnoMGVDWvqPVSv5dcQqiL3nTvhmLzzLzJ4
39
39
  codegraphcontext/core/watcher.py,sha256=nSfkmZueMBHxx8-7zNHScKtaSGfNWrfjWdAe-T0Y2vI,18793
40
40
  codegraphcontext/tools/__init__.py,sha256=iqQPEkDlGXO7rAaLNQrS_ZAbLSklBSK-OowpARq0mig,41
41
41
  codegraphcontext/tools/advanced_language_query_tool.py,sha256=8pRVMg-Tbp3JrbN31McyTbum91gJPlrjHFCFVJ78coA,4169
42
- codegraphcontext/tools/code_finder.py,sha256=ZjHoOLYeC5Xc8N4VU5APqGiATzk9AB5gX9u9wVl0SEY,72040
42
+ codegraphcontext/tools/code_finder.py,sha256=d8RK2HQVPD4EQ_ZmhSsEhU6AY0NoYPnIjROwZpGEB78,73651
43
43
  codegraphcontext/tools/graph_builder.py,sha256=uPHc4q8u7NQ7lSceRm4IwN3nLxgq9C0MvQtkY8HA9CE,57271
44
- codegraphcontext/tools/package_resolver.py,sha256=d2lkHNguvL_QRihD2bbBN-aMGrXRgsK-8pLWkci3nFk,7366
45
- codegraphcontext/tools/report_generator.py,sha256=HLCT6sUmYZxouSXsk0vc1ILV-j2jJjer_RXHTV5MskM,15377
44
+ codegraphcontext/tools/package_resolver.py,sha256=j6s4H5VnourUdJ-CkA3kCOc-Vdewnjc-PNDshAPCiaU,7802
45
+ codegraphcontext/tools/report_generator.py,sha256=3tH83_24nm-pWcY_oXUeLB2jYtGGZmkiBb_oyuhS4WM,16114
46
46
  codegraphcontext/tools/scip_indexer.py,sha256=8ZHaooZeQgTtO_wRI0VwiMgIQF1QNAg7o6Xx5VGj3BM,39112
47
47
  codegraphcontext/tools/scip_pb2.py,sha256=jSMLR7ud4eaKrIkQN-XDWNaFCyUaueafUm9WgOhjqRA,98270
48
48
  codegraphcontext/tools/system.py,sha256=RCUraHQOT-M146CwLM3W1GcBHsAvuans1puL9SPakxs,6647
@@ -52,9 +52,9 @@ codegraphcontext/tools/datasources/__init__.py,sha256=EDY0_s7DJPRzJhfmYUz3Pxv0b6
52
52
  codegraphcontext/tools/datasources/cassandra_ingester.py,sha256=sgbskSkvE98qtOprgzkbVSEIasZWe4WXOzaCiRR2c_Q,3060
53
53
  codegraphcontext/tools/datasources/mysql_ingester.py,sha256=uWstOv7mOEws1pmT8uAsEBkoOGM1FXOWt_2VCE-tZeg,3991
54
54
  codegraphcontext/tools/datasources/redis_ingester.py,sha256=mVUKYmr_v6g7wYaavmQscEHGi_CPZrxYLjfkldc6cWE,3788
55
- codegraphcontext/tools/handlers/analysis_handlers.py,sha256=wasVt3k0fUwNZn6gr3tvD29cJ3IY5Qqk26B7dkE_2XM,14104
56
- codegraphcontext/tools/handlers/indexing_handlers.py,sha256=EDPZOcT4LwhqYrvytoN_NvpOPxTWprLmsc67h5HXmGg,6472
57
- codegraphcontext/tools/handlers/management_handlers.py,sha256=D06vHxhtWeRlyzeFmIDcSNw5qrilLW5sO47HsDdrGcI,17392
55
+ codegraphcontext/tools/handlers/analysis_handlers.py,sha256=8pZAcNNw0CFJ4LfuYqcJ-4cnS-X7_4sKWwd6S7EUGos,14155
56
+ codegraphcontext/tools/handlers/indexing_handlers.py,sha256=sKtbP19EX694zs-6rsVkhwihGwSIWaKjm4N41jMXP2M,7437
57
+ codegraphcontext/tools/handlers/management_handlers.py,sha256=19-fCuMuY5oHXQ9O4F1qG4ZsKKsGpSGbxxA1gH4AE-g,17419
58
58
  codegraphcontext/tools/handlers/query_handlers.py,sha256=mqI7pfuSRc92ChAS5IGtrkPyhpPeh-dg-ewVnysx5nw,4643
59
59
  codegraphcontext/tools/handlers/watcher_handlers.py,sha256=po0NP3WnUqJAiDyZW9pz2g2V-itnazI10cXQJuc1UZ8,3809
60
60
  codegraphcontext/tools/indexing/__init__.py,sha256=exCNeB4UYhKhNSoEBNta_EZ51Kdze4iK4e3oWH8B4X8,126
@@ -66,7 +66,7 @@ codegraphcontext/tools/indexing/pre_scan.py,sha256=St7Hc3-NFTMYF39vWneJL6iIRIc78
66
66
  codegraphcontext/tools/indexing/sanitize.py,sha256=IaTP7Aztso3SsvjNZMEqJ1iktV4Tl75hXcYnCWBGpLs,1490
67
67
  codegraphcontext/tools/indexing/schema.py,sha256=AcHaqxDi9d5nGjZdg5S6mbjhHo98UYPRfcz1ZpvNEy4,9157
68
68
  codegraphcontext/tools/indexing/schema_contract.py,sha256=23ok37ASjvOsU8S-sqyRiKIkGhcUxQ8PSErbBmylggU,1815
69
- codegraphcontext/tools/indexing/scip_pipeline.py,sha256=Eb7VKN13vPOJ_Ag34c1yQ4c41TGvsemjMKFHnSjBuCg,12724
69
+ codegraphcontext/tools/indexing/scip_pipeline.py,sha256=OLbf0rhs1XU4LTQ6B3mS1-ngbPEKIN69o59rw9Pbk_E,13128
70
70
  codegraphcontext/tools/indexing/vector_resolver.py,sha256=0D0QClro7nx9Wx6hbbFlOsO4dqgcTrwPMqE0CsJK01o,5477
71
71
  codegraphcontext/tools/indexing/persistence/__init__.py,sha256=hFRlNgiYS7uN9X4e4xfP3c1BvcYSxpmq0k_uaDRn2Fg,121
72
72
  codegraphcontext/tools/indexing/persistence/utils.py,sha256=OtndHpE_R14mPz5i-Bo9mA7NsoIuYnCNER-oIwxDOYA,2028
@@ -75,9 +75,9 @@ codegraphcontext/tools/indexing/resolution/__init__.py,sha256=BR57mVydpzjOnFSbGb
75
75
  codegraphcontext/tools/indexing/resolution/calls.py,sha256=Uamtm8DXJnwlnTkdzYfu6G74MOcEyQ4njasweE7Nydo,130762
76
76
  codegraphcontext/tools/indexing/resolution/inheritance.py,sha256=xataBg0Lvgj3Sexgxx434TmwgRpvODaQs5HQYEVxPVs,21192
77
77
  codegraphcontext/tools/indexing/resolution/post_resolution.py,sha256=OzmbbmLwIbKb5wm1RodoyvDST3d35A2e35ruDKBQDF4,10859
78
- codegraphcontext/tools/languages/c.py,sha256=1lmae7RMttJPJRv1YdRNv8jVn2bU35JxFy_JlMARJyo,30601
79
- codegraphcontext/tools/languages/cpp.py,sha256=_ZeKPu5c7dR5IhGMWJnklcQOD4DbEMSF_PuntdEEmio,28378
80
- codegraphcontext/tools/languages/csharp.py,sha256=JvHdg-IsXBm7DC5KtxVJyFMWnQw6FnspCDrAoUqe1H0,23988
78
+ codegraphcontext/tools/languages/c.py,sha256=quo2j0PCTlSOJctqqF_5zKeisERvTDyKuPtz_qvFgQA,31618
79
+ codegraphcontext/tools/languages/cpp.py,sha256=41kDYWbuH6hjNAZyCEH9RhZBNCUz8kQyccw3Ppd-efQ,31389
80
+ codegraphcontext/tools/languages/csharp.py,sha256=oiSBmEf96mIU-2u-0OVXyx3QTn1_lOv-XLPc6aujarA,27502
81
81
  codegraphcontext/tools/languages/css.py,sha256=aN1Nm8vHmfV0J0jQ-_q5x0YDTOtLV2IW-5lp7WtGG5o,3789
82
82
  codegraphcontext/tools/languages/dart.py,sha256=yjqVA6XwoADrZab95p6wva0jT-PAGw_x90LR-cp2BDk,27160
83
83
  codegraphcontext/tools/languages/elisp.py,sha256=QVxJq328bR4DsQK8Vs8b9oxthx1Wm_BS1dsCSjD_P8I,22717
@@ -87,7 +87,7 @@ codegraphcontext/tools/languages/gradle.py,sha256=WA1nQTi-JMHv5vLTJ35ECW0dQCb4PE
87
87
  codegraphcontext/tools/languages/haskell.py,sha256=YyslUwcanFs00IinFrj7_TJ2IY2Q97NYsG2Y-9qqolU,19888
88
88
  codegraphcontext/tools/languages/html.py,sha256=cjzAH55-JYbObGcMiqjuYD6X0Cbs1Kj5ixcZv9BzMns,5595
89
89
  codegraphcontext/tools/languages/java.py,sha256=dpyHBGPhL1FaYP_unh-ncRDOlmmA2nBqnkLc1tacYm0,52024
90
- codegraphcontext/tools/languages/javascript.py,sha256=Pz2HlHx2YxChx6yA76BNZDNHpjEBjshmSmW8bavKUg0,26941
90
+ codegraphcontext/tools/languages/javascript.py,sha256=LR0W2BKZANQaQlNiIshORmJ08IAVVf9Pv1a8-6pILDo,30197
91
91
  codegraphcontext/tools/languages/kotlin.py,sha256=8W367uThQzuTqbFzobUnjm_VEGf_jhLox_fmo13wYsc,88644
92
92
  codegraphcontext/tools/languages/lua.py,sha256=TxpOxFd47E4hKwkJpO7b_dMvWveYd4Eaqp1n5SnN-zM,16726
93
93
  codegraphcontext/tools/languages/maven.py,sha256=yQjrdSRZJPy6tUopZ7x5qYp8Rtbug4PPbBjCdl9FMDY,6393
@@ -95,7 +95,7 @@ codegraphcontext/tools/languages/mybatis.py,sha256=T6mVBCg7_5xOobBIAqZg9oHtZQy7s
95
95
  codegraphcontext/tools/languages/perl.py,sha256=vjz65VFoKLsql2gyaT44B3R0sieloQmxO7fHMp8aqLA,12837
96
96
  codegraphcontext/tools/languages/php.py,sha256=WwdFmWvBxQX8rsqHFCr8tdrz_W3pWv_COp1Bw-xiGiE,29804
97
97
  codegraphcontext/tools/languages/python.py,sha256=6Fd05Hxc-jVRTa6t0wYuk8-yZ-0MdrRW3xo1TOucxEk,32714
98
- codegraphcontext/tools/languages/ruby.py,sha256=GCU2AOfU8H4nNzunf0F4OU_aLr3GGIWcSJgVhO89up8,23428
98
+ codegraphcontext/tools/languages/ruby.py,sha256=vkSNS6JOmmVKxG9eDPo5rNd6fNACUScrEjGBZbHg8Fc,24961
99
99
  codegraphcontext/tools/languages/rust.py,sha256=o_uhEL_a4REPYXqdH4tljucyXMs5ModaMfeT-sanTBc,15560
100
100
  codegraphcontext/tools/languages/scala.py,sha256=TmH_DK7baoo7_eMYdu9e7Cqb0ddVpibrhUO5TQD4AsI,23820
101
101
  codegraphcontext/tools/languages/sfc_common.py,sha256=BLCqNO8EoLSdxt1u-1XvrkUotgWHJkXts6w1NQXH6XU,8567
@@ -113,7 +113,7 @@ codegraphcontext/tools/query_tool_languages/dart_toolkit.py,sha256=W5ClOZ-KCRFmw
113
113
  codegraphcontext/tools/query_tool_languages/elisp_toolkit.py,sha256=fH8T5HKULMSgq8m46kCUi7Ql1lglTH5taQemJLCrKi0,2591
114
114
  codegraphcontext/tools/query_tool_languages/go_toolkit.py,sha256=wK-QMk8fBlJjvnvp9nK9hRq-GbcfsdRaY35E65uacME,273
115
115
  codegraphcontext/tools/query_tool_languages/haskell_toolkit.py,sha256=3GMwoSAk8X1nK4Yjz8YMhc_m7bStEI7wYOu0tgTkZl4,283
116
- codegraphcontext/tools/query_tool_languages/java_toolkit.py,sha256=hlqJ9mZ-jgnAN6tCx4CztjfHVFhCFEYVrNnMMxaLelA,9418
116
+ codegraphcontext/tools/query_tool_languages/java_toolkit.py,sha256=jjHs_lMgMFu8AgOcUvNrWJSdGSKTZuSYRyjzuESWFMo,9423
117
117
  codegraphcontext/tools/query_tool_languages/javascript_toolkit.py,sha256=AZNo63prnaETy1BXy1en40brGODIpcrH6GdmSohp5co,289
118
118
  codegraphcontext/tools/query_tool_languages/perl_toolkit.py,sha256=vkd9O5YCXIgSbq2dYVvWT4T8BvL66Qo98TRf0kD5w-c,277
119
119
  codegraphcontext/tools/query_tool_languages/python_toolkit.py,sha256=wijYvL7g4wevJhrtbk5asLC8oxIlpZ8IxidRi4HChKM,281
@@ -173,9 +173,9 @@ codegraphcontext/viz/dist/wasm/tree-sitter-typescript.wasm,sha256=hRVATc7tOOHthq
173
173
  codegraphcontext/viz/dist/wasm/tree-sitter.wasm,sha256=CCeVuI_hXktkBD-DW01xYPv5XoAYVRIqzJUJI5te-RY,196763
174
174
  codegraphcontext/viz/dist/wasm/web-tree-sitter.js,sha256=DIaCNqRylrT_PBVw8g4ImeSnhP9uXNe_ycOlUiVGPko,153666
175
175
  codegraphcontext/viz/dist/wasm/web-tree-sitter.wasm,sha256=CCeVuI_hXktkBD-DW01xYPv5XoAYVRIqzJUJI5te-RY,196763
176
- codegraphcontext-0.5.6.dist-info/licenses/LICENSE,sha256=rh8M-bJpQYJnw2vtRVgt0t7piMZXh5QzaKeNEI0vqqA,1061
177
- codegraphcontext-0.5.6.dist-info/METADATA,sha256=bcijz1PwTgr-fzCqt31srVpSUK9oP966UKQkDQzhF9s,32847
178
- codegraphcontext-0.5.6.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
179
- codegraphcontext-0.5.6.dist-info/entry_points.txt,sha256=LCxWCWMshdvYGoHBPuQZ8C-e4CiNSHCLXofrNSGHkoE,103
180
- codegraphcontext-0.5.6.dist-info/top_level.txt,sha256=CBgc6LAPZIO5FS0nSYYkylDifHsZTIqw3Gf5UwDxeGI,17
181
- codegraphcontext-0.5.6.dist-info/RECORD,,
176
+ codegraphcontext-0.5.7.dist-info/licenses/LICENSE,sha256=rh8M-bJpQYJnw2vtRVgt0t7piMZXh5QzaKeNEI0vqqA,1061
177
+ codegraphcontext-0.5.7.dist-info/METADATA,sha256=pb9kZrMtqIxBsrCVhKXy0zX--PZTWRc6jE0Px6G7dyg,32892
178
+ codegraphcontext-0.5.7.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
179
+ codegraphcontext-0.5.7.dist-info/entry_points.txt,sha256=LCxWCWMshdvYGoHBPuQZ8C-e4CiNSHCLXofrNSGHkoE,103
180
+ codegraphcontext-0.5.7.dist-info/top_level.txt,sha256=CBgc6LAPZIO5FS0nSYYkylDifHsZTIqw3Gf5UwDxeGI,17
181
+ codegraphcontext-0.5.7.dist-info/RECORD,,