codingest 0.2.4__tar.gz → 0.2.6__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 (111) hide show
  1. {codingest-0.2.4 → codingest-0.2.6}/Cargo.lock +12 -10
  2. {codingest-0.2.4 → codingest-0.2.6}/Cargo.toml +63 -6
  3. {codingest-0.2.4 → codingest-0.2.6}/PKG-INFO +5 -5
  4. {codingest-0.2.4 → codingest-0.2.6}/README.md +3 -3
  5. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/Cargo.toml +10 -1
  6. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/builder/load.rs +56 -24
  7. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/builder/other_edges.rs +389 -14
  8. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/docs/mod.rs +175 -14
  9. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/parsers/cpp.rs +115 -1
  10. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/parsers/csharp.rs +296 -27
  11. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/parsers/css.rs +41 -1
  12. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/parsers/dart.rs +128 -21
  13. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/parsers/html.rs +56 -3
  14. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/parsers/php.rs +234 -13
  15. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/parsers/rust_lang.rs +161 -1
  16. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/parsers/swift.rs +67 -1
  17. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/rev.rs +117 -20
  18. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/tests/goldens/README.md +189 -0
  19. codingest-0.2.6/crates/codingest/tests/goldens/cpp_extern_c.sha256 +1 -0
  20. codingest-0.2.6/crates/codingest/tests/goldens/csharp_using_alias.sha256 +1 -0
  21. codingest-0.2.6/crates/codingest/tests/goldens/dart_part_of.sha256 +1 -0
  22. codingest-0.2.6/crates/codingest/tests/goldens/docs_mdx.sha256 +1 -0
  23. codingest-0.2.6/crates/codingest/tests/goldens/dup_minified_assets.sha256 +1 -0
  24. codingest-0.2.6/crates/codingest/tests/goldens/go_interface.sha256 +1 -0
  25. codingest-0.2.6/crates/codingest/tests/goldens/html_js_lang_group.sha256 +1 -0
  26. codingest-0.2.6/crates/codingest/tests/goldens/java_javadoc.sha256 +1 -0
  27. codingest-0.2.6/crates/codingest/tests/goldens/julia_basic.sha256 +1 -0
  28. codingest-0.2.6/crates/codingest/tests/goldens/php_group_use.sha256 +1 -0
  29. codingest-0.2.6/crates/codingest/tests/goldens/py_src_layout.sha256 +1 -0
  30. codingest-0.2.6/crates/codingest/tests/goldens/rust_inline_mod.sha256 +1 -0
  31. codingest-0.2.6/crates/codingest/tests/goldens/swift_basic.sha256 +1 -0
  32. codingest-0.2.6/crates/codingest/tests/goldens/web_served_root.sha256 +1 -0
  33. codingest-0.2.6/crates/codingest/tests/grammar_kinds.rs +403 -0
  34. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/tests/parity.rs +178 -0
  35. codingest-0.2.6/crates/codingest/tests/traversal_semantics.rs +138 -0
  36. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest-cli/Cargo.toml +1 -1
  37. codingest-0.2.6/crates/codingest-cli/skills/codingest-code-review/references/queries.md +84 -0
  38. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest-cli/src/query.rs +36 -2
  39. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest-cli/tests/exit_codes.rs +15 -0
  40. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest-mcp/Cargo.toml +1 -1
  41. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest-py/Cargo.toml +3 -3
  42. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest-py/src/lib.rs +1 -1
  43. {codingest-0.2.4 → codingest-0.2.6}/pyproject.toml +2 -2
  44. codingest-0.2.4/crates/codingest/tests/goldens/docs_mdx.sha256 +0 -1
  45. codingest-0.2.4/crates/codingest/tests/goldens/dup_minified_assets.sha256 +0 -1
  46. codingest-0.2.4/crates/codingest/tests/goldens/html_js_lang_group.sha256 +0 -1
  47. codingest-0.2.4/crates/codingest/tests/goldens/julia_basic.sha256 +0 -1
  48. codingest-0.2.4/crates/codingest/tests/grammar_kinds.rs +0 -180
  49. codingest-0.2.4/crates/codingest-cli/skills/codingest-code-review/references/queries.md +0 -49
  50. {codingest-0.2.4 → codingest-0.2.6}/LICENSE +0 -0
  51. {codingest-0.2.4 → codingest-0.2.6}/codingest/__init__.py +0 -0
  52. {codingest-0.2.4 → codingest-0.2.6}/codingest/__init__.pyi +0 -0
  53. {codingest-0.2.4 → codingest-0.2.6}/codingest/cli.py +0 -0
  54. {codingest-0.2.4 → codingest-0.2.6}/codingest/mcp_server.py +0 -0
  55. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/bin/codingest_bench.rs +0 -0
  56. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/bin/codingest_stats.rs +0 -0
  57. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/builder/call_edges.rs +0 -0
  58. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/builder/js_workspace.rs +0 -0
  59. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/builder/load/edge_frames.rs +0 -0
  60. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/builder/load/entity_frames.rs +0 -0
  61. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/builder/mod.rs +0 -0
  62. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/builder/routes/django.rs +0 -0
  63. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/builder/routes/fastapi.rs +0 -0
  64. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/builder/routes/flask.rs +0 -0
  65. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/builder/routes/mod.rs +0 -0
  66. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/builder/semantic_edges.rs +0 -0
  67. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/builder/type_edges.rs +0 -0
  68. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/cross_lang.rs +0 -0
  69. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/docs/rst.rs +0 -0
  70. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/lib.rs +0 -0
  71. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/manifest/mod.rs +0 -0
  72. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/models.rs +0 -0
  73. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/parsers/agc.rs +0 -0
  74. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/parsers/go.rs +0 -0
  75. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/parsers/java.rs +0 -0
  76. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/parsers/julia.rs +0 -0
  77. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/parsers/mod.rs +0 -0
  78. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/parsers/python.rs +0 -0
  79. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/parsers/r.rs +0 -0
  80. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/parsers/registry.rs +0 -0
  81. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/parsers/shared.rs +0 -0
  82. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/parsers/typescript.rs +0 -0
  83. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/src/repo.rs +0 -0
  84. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/tests/agc_apollo.rs +0 -0
  85. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/tests/goldens/agc_basic.sha256 +0 -0
  86. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/tests/goldens/cpp_include.sha256 +0 -0
  87. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/tests/goldens/cross_ts_py.sha256 +0 -0
  88. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/tests/goldens/dart_import.sha256 +0 -0
  89. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/tests/goldens/docs_ext_collide.sha256 +0 -0
  90. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/tests/goldens/py_basic.sha256 +0 -0
  91. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/tests/goldens/py_import.sha256 +0 -0
  92. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/tests/goldens/py_inheritance.sha256 +0 -0
  93. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/tests/goldens/py_nested_defs.sha256 +0 -0
  94. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/tests/goldens/py_routes_dup.sha256 +0 -0
  95. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/tests/goldens/r_basic.sha256 +0 -0
  96. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/tests/goldens/rust_import.sha256 +0 -0
  97. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/tests/goldens/rust_xfile.sha256 +0 -0
  98. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/tests/goldens/ts_callback.sha256 +0 -0
  99. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/tests/goldens/ts_closure_scope.sha256 +0 -0
  100. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/tests/goldens/ts_hof_binding.sha256 +0 -0
  101. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest/tests/goldens/ts_monorepo.sha256 +0 -0
  102. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest-cli/skills/codingest-code-review/SKILL.md +0 -0
  103. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest-cli/skills/codingest-code-review/references/mcp-upgrade.md +0 -0
  104. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest-cli/skills/codingest-code-review/references/public-repositories.md +0 -0
  105. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest-cli/src/code_tree_cli.rs +0 -0
  106. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest-cli/src/lib.rs +0 -0
  107. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest-cli/src/main.rs +0 -0
  108. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest-cli/src/skill.rs +0 -0
  109. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest-cli/src/skill_assets.rs +0 -0
  110. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest-mcp/src/lib.rs +0 -0
  111. {codingest-0.2.4 → codingest-0.2.6}/crates/codingest-mcp/src/main.rs +0 -0
@@ -308,16 +308,18 @@ dependencies = [
308
308
 
309
309
  [[package]]
310
310
  name = "codingest"
311
- version = "0.2.4"
311
+ version = "0.2.6"
312
312
  dependencies = [
313
313
  "aho-corasick",
314
314
  "base64 0.22.1",
315
315
  "kglite",
316
+ "proc-macro2",
316
317
  "rayon",
317
318
  "regex",
318
319
  "serde",
319
320
  "serde_json",
320
321
  "sha2",
322
+ "syn 2.0.119",
321
323
  "tempfile",
322
324
  "toml",
323
325
  "tree-sitter",
@@ -342,7 +344,7 @@ dependencies = [
342
344
 
343
345
  [[package]]
344
346
  name = "codingest-cli"
345
- version = "0.2.4"
347
+ version = "0.2.6"
346
348
  dependencies = [
347
349
  "anyhow",
348
350
  "clap",
@@ -354,7 +356,7 @@ dependencies = [
354
356
 
355
357
  [[package]]
356
358
  name = "codingest-mcp"
357
- version = "0.2.4"
359
+ version = "0.2.6"
358
360
  dependencies = [
359
361
  "anyhow",
360
362
  "codingest",
@@ -363,7 +365,7 @@ dependencies = [
363
365
 
364
366
  [[package]]
365
367
  name = "codingest-py"
366
- version = "0.2.4"
368
+ version = "0.2.6"
367
369
  dependencies = [
368
370
  "codingest",
369
371
  "codingest-cli",
@@ -1390,9 +1392,9 @@ dependencies = [
1390
1392
 
1391
1393
  [[package]]
1392
1394
  name = "kglite"
1393
- version = "0.16.2"
1395
+ version = "0.16.6"
1394
1396
  source = "registry+https://github.com/rust-lang/crates.io-index"
1395
- checksum = "e8d8175004ae15c78c32ea60abfa7515dc384ceac8c9dbeb2272884bd24b2401"
1397
+ checksum = "e79966b681151c31fd9062a0e219cb69965b19715354201d408b6aed8a257116"
1396
1398
  dependencies = [
1397
1399
  "bzip2",
1398
1400
  "chrono",
@@ -1425,9 +1427,9 @@ dependencies = [
1425
1427
 
1426
1428
  [[package]]
1427
1429
  name = "kglite-mcp-server"
1428
- version = "0.16.2"
1430
+ version = "0.16.6"
1429
1431
  source = "registry+https://github.com/rust-lang/crates.io-index"
1430
- checksum = "79ff376e0f81bcb1312256c762d3cac9a7665f6340d72f8503b266b0e25eece2"
1432
+ checksum = "889deddaa25ae6358bccd00ebeb43cd2ed07ee35a6f3d3624aeb625c75260470"
1431
1433
  dependencies = [
1432
1434
  "anyhow",
1433
1435
  "bytes",
@@ -1529,9 +1531,9 @@ dependencies = [
1529
1531
 
1530
1532
  [[package]]
1531
1533
  name = "mcp-methods"
1532
- version = "0.4.4"
1534
+ version = "0.4.6"
1533
1535
  source = "registry+https://github.com/rust-lang/crates.io-index"
1534
- checksum = "d6d41f37fa137ea40a0cd505729e41ae1156ed119dcf8f240210579889c5ba29"
1536
+ checksum = "d413193821b58fa600d8b110fc5b7141cf71fcf367af0dbda77dca86c37eb1c8"
1535
1537
  dependencies = [
1536
1538
  "anyhow",
1537
1539
  "clap",
@@ -3,7 +3,7 @@ members = ["crates/codingest", "crates/codingest-cli", "crates/codingest-mcp", "
3
3
  resolver = "2"
4
4
 
5
5
  [workspace.package]
6
- version = "0.2.4"
6
+ version = "0.2.6"
7
7
  edition = "2021"
8
8
  rust-version = "1.88"
9
9
  license = "MIT"
@@ -12,8 +12,65 @@ repository = "https://github.com/kkollsga/codingest"
12
12
  [workspace.dependencies]
13
13
  base64 = "0.22"
14
14
  sha2 = "0.11"
15
- # kglite engine + MCP server — imported as crates.io libraries. The 0.16.2
16
- # floor fixes the edge-property type registry: `add_connections` the ONLY
15
+ # kglite engine + MCP server — imported as crates.io libraries. The 0.16.6
16
+ # floor picks up the advisory traversal caps (our reported LIMIT bug: capped
17
+ # pass + exact uncapped retry), the BREAKING var-length trail-semantics fixes
18
+ # (a closed trail re-emits its source; a segment's relationships are visible to
19
+ # its clause's uniqueness check), and per-section .kgl CRC32s. Zero source
20
+ # changes: the Rust API delta is purely additive for every symbol we name, and
21
+ # add_nodes/add_connections behavior is unchanged, so the frozen goldens hold.
22
+ # The .kgl format gains a `section_digests` metadata field — additive in BOTH
23
+ # directions (serde default/skip), so unlike 0.16.2 a stale .kgl needs NO
24
+ # rebuild; saves pay the CRC pass (upstream ~+11-15%). The semantics change IS
25
+ # observable in query answers on cyclic graphs — pinned by
26
+ # crates/codingest/tests/traversal_semantics.rs, which is red on 0.16.5 and
27
+ # green on 0.16.6 by design.
28
+ #
29
+ # History — the 0.16.5 floor was a LOCKSTEP REFRESH, not a fix we needed:
30
+ # nothing in 0.16.3-0.16.5
31
+ # reaches a path codingest exercises, and the evidence is the gate rather than
32
+ # the changelog. `golden_parity` is green and `kgl_bytes_are_stable_across_builds`
33
+ # holds, so the frozen goldens are byte-identical across the bump; the fixture
34
+ # corpus builds 405 nodes / 616 edges docs-on and 398 / 596 docs-off on both
35
+ # 0.16.2 and 0.16.5. Perf is flat: three interleaved A/B pairs per docs mode on
36
+ # corpus ad8b3084…, control `defs_per_file` moving +0.0%, every cell flat except
37
+ # `varlen_callers_1_3` at -9.7% in BOTH modes (0.031 -> 0.028 ms) — a small
38
+ # consistent win, plausibly the PropMap refcount clone below. A same-machine
39
+ # before/after pair taken 20 minutes apart instead read +50% on three cells with
40
+ # the control at +15%; that is the instrument, and the interleaved pairs are why
41
+ # the flat reading is the one recorded (doctrine R11).
42
+ #
43
+ # What each release contains, and why none of it lands on us:
44
+ # * 0.16.5 is a BREAKING Rust API change — node, relationship and map
45
+ # properties are a `kglite::datatypes::PropMap`, not a
46
+ # `BTreeMap<String, Value>`. We are not exposed: codingest names neither
47
+ # `NodeValue::properties` nor `RelValue::properties`, and its one
48
+ # `Value::Map` site (docs/mod.rs) matches the wildcard `Value::Map(_)` to
49
+ # stringify a non-scalar, which binds nothing. The workspace compiles clean
50
+ # with no source change. No file format moved — postcard's map framing is
51
+ # identical — which is what lets the goldens hold. Also in 0.16.5:
52
+ # relationship constraints, CDC before-images and selectors, and a
53
+ # re-save determinism fix. codingest declares no constraints, consumes no
54
+ # CDC, and only ever writes FRESH saves, so the re-save fix (and its
55
+ # property-name reordering) cannot reach our bytes.
56
+ # * 0.16.4 adds the opt-in parallel Cypher runtime (off by default and NOT
57
+ # exposed by the MCP server), property-type constraints, and CDC v1. Its
58
+ # one format note — a `.kgl` carrying a property-type constraint does not
59
+ # load on 0.16.3 or earlier — is inert here because codingest declares
60
+ # none, so our files stay readable by an older engine. That is why the
61
+ # Python floor moves for LOCKSTEP, not because the format broke: the
62
+ # `test_kglite_cargo_and_python_floors_are_in_lockstep` gate requires
63
+ # Cargo.toml, pyproject.toml and ci.yml to state one floor, and a
64
+ # "the reader could stay lower" argument is exactly the drift it exists to
65
+ # stop.
66
+ # * 0.16.3 is MCP-side: `reload_graph`, `extensions.graph_watch`, and
67
+ # `extensions.tools_allow`. It also carries mcp-methods 0.4.5, which makes
68
+ # the GitHub tools require an explicit `builtins.github: true` — codingest-mcp
69
+ # registers no GitHub tools and ships no manifest declaring them, so its
70
+ # agent-visible tool surface is unchanged.
71
+ #
72
+ # Floor history below; the 0.16.2 entry is the last one that changed our output.
73
+ # The 0.16.2 floor fixed the edge-property type registry: `add_connections` — the ONLY
17
74
  # edge path codingest uses (builder/load.rs, other_edges.rs, cross_lang.rs,
18
75
  # docs/mod.rs) — registered every property as "Unknown" instead of its observed
19
76
  # type. Measured on a two-function Python corpus: `CALL
@@ -34,7 +91,7 @@ sha2 = "0.11"
34
91
  # which kglite 0.15.14 and earlier refuse by version number. Because
35
92
  # `codingest.build()` writes the bytes with the Rust engine compiled in here and
36
93
  # reads them back through the separately-installed Python `kglite` wheel, the
37
- # two sides must be on the same side of that break — hence `>=0.16.2,<0.17` in
94
+ # two sides must be on the same side of that break — hence `>=0.16.6,<0.17` in
38
95
  # pyproject.toml, and hence a reader on 0.15.x is not "old but workable", it is
39
96
  # a hard failure with `FileFormatError`.
40
97
  # 0.16.0 also removed `DirGraph::enable_columnar` from the public API (every
@@ -99,8 +156,8 @@ sha2 = "0.11"
99
156
  # and no disk storage. Neither 0.15.7 nor 0.15.8 changes the `kglite::api` we
100
157
  # call, graph output, property encoding, or `.kgl` serialization.
101
158
  # Keep both libraries aligned so the graph and server share one engine release.
102
- kglite = { version = "0.16.2", default-features = false }
103
- kglite-mcp-server = { version = "0.16.2", default-features = false }
159
+ kglite = { version = "0.16.6", default-features = false }
160
+ kglite-mcp-server = { version = "0.16.6", default-features = false }
104
161
 
105
162
  # Mirror KGLite's release codegen so the standalone builder has
106
163
  # performance parity with the in-tree component (see KGLite
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codingest
3
- Version: 0.2.4
3
+ Version: 0.2.6
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: Programming Language :: Python :: 3
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3.14
12
12
  Classifier: Programming Language :: Rust
13
13
  Classifier: Topic :: Software Development
14
14
  Classifier: Topic :: Software Development :: Libraries
15
- Requires-Dist: kglite>=0.16.2,<0.17
15
+ Requires-Dist: kglite>=0.16.6,<0.17
16
16
  License-File: LICENSE
17
17
  Summary: Build fast, queryable code graphs for AI agents, MCP code review, and local or GitHub repository analysis.
18
18
  Keywords: ai-agents,mcp,code-review,knowledge-graph,code-analysis,github,tree-sitter,cypher
@@ -192,7 +192,7 @@ codingest skill install # code-review Agent Skill
192
192
  ```
193
193
 
194
194
  The wheel bundles every grammar plus the `codingest` and `codingest-mcp`
195
- commands. KGLite ≥0.16.2 is installed automatically as the query/storage
195
+ commands. KGLite ≥0.16.6 is installed automatically as the query/storage
196
196
  engine; its MCP server and the transitive `mcp-methods` framework power the
197
197
  builder-aware Codingest server. Nothing else needs to be installed.
198
198
 
@@ -205,7 +205,7 @@ Python prerequisites.
205
205
  ```toml
206
206
  [dependencies]
207
207
  codingest = "0.2"
208
- kglite = "0.16.2"
208
+ kglite = "0.16.6"
209
209
  ```
210
210
 
211
211
  ```rust
@@ -277,7 +277,7 @@ diagnostic.
277
277
  ## Dependency policy
278
278
 
279
279
  `kglite` and `kglite-mcp-server` use matching crates.io requirements with a
280
- 0.16.2 minimum and a shared lockfile. This keeps the builder, persistence
280
+ 0.16.6 minimum and a shared lockfile. This keeps the builder, persistence
281
281
  handoff, and embedded MCP server on one compatible engine patch line.
282
282
 
283
283
  ## Parity with the (now-removed) in-tree component
@@ -165,7 +165,7 @@ codingest skill install # code-review Agent Skill
165
165
  ```
166
166
 
167
167
  The wheel bundles every grammar plus the `codingest` and `codingest-mcp`
168
- commands. KGLite ≥0.16.2 is installed automatically as the query/storage
168
+ commands. KGLite ≥0.16.6 is installed automatically as the query/storage
169
169
  engine; its MCP server and the transitive `mcp-methods` framework power the
170
170
  builder-aware Codingest server. Nothing else needs to be installed.
171
171
 
@@ -178,7 +178,7 @@ Python prerequisites.
178
178
  ```toml
179
179
  [dependencies]
180
180
  codingest = "0.2"
181
- kglite = "0.16.2"
181
+ kglite = "0.16.6"
182
182
  ```
183
183
 
184
184
  ```rust
@@ -250,7 +250,7 @@ diagnostic.
250
250
  ## Dependency policy
251
251
 
252
252
  `kglite` and `kglite-mcp-server` use matching crates.io requirements with a
253
- 0.16.2 minimum and a shared lockfile. This keeps the builder, persistence
253
+ 0.16.6 minimum and a shared lockfile. This keeps the builder, persistence
254
254
  handoff, and embedded MCP server on one compatible engine patch line.
255
255
 
256
256
  ## Parity with the (now-removed) in-tree component
@@ -10,7 +10,7 @@ keywords = ["knowledge-graph", "code-analysis", "tree-sitter", "cypher", "kglite
10
10
  categories = ["development-tools", "parser-implementations"]
11
11
 
12
12
  # Requires the kglite 0.15 engine API (`kglite::api::code_entities`); the
13
- # workspace pins the current engine floor (0.16.2).
13
+ # workspace pins the current engine floor (0.16.6).
14
14
 
15
15
  [lib]
16
16
  name = "codingest"
@@ -75,3 +75,12 @@ aho-corasick = "1.1.4"
75
75
 
76
76
  [dev-dependencies]
77
77
  tempfile = "3.27.0"
78
+ # `tests/grammar_kinds.rs` parses the parser sources as Rust instead of
79
+ # regexing them, which is what makes `match node.kind() { "lit" => … }` arms
80
+ # exactly extractable (a regex over arm bodies false-positives on every other
81
+ # string literal in the body). `full` = item/statement/expression syntax,
82
+ # `visit` = the borrowing visitor the extractor implements. `span-locations`
83
+ # on proc-macro2 is what gives the failure message a real `file:line` for each
84
+ # offending literal; without it every span reports line 1.
85
+ syn = { version = "2.0.119", features = ["full", "visit"] }
86
+ proc-macro2 = { version = "1.0.107", features = ["span-locations"] }
@@ -944,10 +944,16 @@ impl<'a> LoadPipeline<'a> {
944
944
  defaults.iter().find(|(_, exists)| *exists).map(|(n, _)| *n)
945
945
  };
946
946
 
947
- if !type_out.implements.is_empty() {
948
- // Route IMPLEMENTS per-row based on the resolved source/target types.
949
- // Python's _add_typed_connections does the equivalent via name_to_qname.
950
- let mut qname_to_type: HashMap<String, &'static str> = HashMap::new();
947
+ // `qualified_name` (and bare name) → the node type that symbol was
948
+ // actually inserted as. IMPLEMENTS and EXTENDS both route per-row
949
+ // through this map: `add_connections` is given ONE label per endpoint,
950
+ // so handing it a label the endpoint was not stored under makes it
951
+ // mint a `_provisional` stub of that wrong type beside the real node
952
+ // and hang the edge off the phantom. Julia's `struct Circle <: Shape`
953
+ // is the shape that proves it — a Struct child under a Class parent.
954
+ // Python's _add_typed_connections does the equivalent via name_to_qname.
955
+ let mut qname_to_type: HashMap<String, &'static str> = HashMap::new();
956
+ if !type_out.implements.is_empty() || !type_out.extends.is_empty() {
951
957
  for c in &result.classes {
952
958
  let nt = super::class_node_type(&c.kind);
953
959
  qname_to_type.insert(c.qualified_name.clone(), nt);
@@ -967,35 +973,27 @@ impl<'a> LoadPipeline<'a> {
967
973
  qname_to_type.insert(i.name.clone(), nt);
968
974
  }
969
975
  // External stubs we just inserted: traits → Trait, classes → Class/Struct.
970
- let ext_trait_type = if graph.has_node_type("Trait") {
971
- Some("Trait")
972
- } else if graph.has_node_type("Protocol") {
973
- Some("Protocol")
974
- } else if graph.has_node_type("Interface") {
975
- Some("Interface")
976
- } else {
977
- None
978
- };
976
+ let ext_trait_type = pick(&[
977
+ ("Trait", has_trait),
978
+ ("Protocol", has_protocol),
979
+ ("Interface", has_interface),
980
+ ]);
979
981
  if let Some(nt) = ext_trait_type {
980
982
  for ext in &type_out.external_traits {
981
983
  qname_to_type.insert(ext.qualified_name.clone(), nt);
982
984
  qname_to_type.insert(ext.name.clone(), nt);
983
985
  }
984
986
  }
985
- let ext_class_type = if graph.has_node_type("Class") {
986
- Some("Class")
987
- } else if graph.has_node_type("Struct") {
988
- Some("Struct")
989
- } else {
990
- None
991
- };
987
+ let ext_class_type = pick(&[("Class", has_class), ("Struct", has_struct)]);
992
988
  if let Some(nt) = ext_class_type {
993
989
  for ext in &type_out.external_classes {
994
990
  qname_to_type.insert(ext.qualified_name.clone(), nt);
995
991
  qname_to_type.insert(ext.name.clone(), nt);
996
992
  }
997
993
  }
994
+ }
998
995
 
996
+ if !type_out.implements.is_empty() {
999
997
  let default_src =
1000
998
  pick(&[("Class", has_class), ("Struct", has_struct)]).unwrap_or("Class");
1001
999
  let default_tgt = pick(&[
@@ -1048,15 +1046,49 @@ impl<'a> LoadPipeline<'a> {
1048
1046
  }
1049
1047
  }
1050
1048
  if !type_out.extends.is_empty() {
1051
- let src = pick(&[("Class", has_class), ("Struct", has_struct)]);
1052
- if let Some(src) = src {
1049
+ // Route EXTENDS per-row exactly like IMPLEMENTS above. The two
1050
+ // endpoints of an inheritance edge need not share a node type:
1051
+ // julia's `struct Circle <: Shape` is Struct → Class, and passing
1052
+ // one picked label for BOTH ends matched `child_name` against the
1053
+ // wrong type and attached the edge to a `_provisional` Class stub
1054
+ // standing beside the real Struct node.
1055
+ let default_type =
1056
+ pick(&[("Class", has_class), ("Struct", has_struct)]).unwrap_or("Class");
1057
+
1058
+ let mut by_pair: BTreeMap<
1059
+ (&'static str, &'static str),
1060
+ Vec<&super::type_edges::ExtendsEdge>,
1061
+ > = BTreeMap::new();
1062
+ for edge in &type_out.extends {
1063
+ let src = qname_to_type
1064
+ .get(&edge.child_name)
1065
+ .copied()
1066
+ .unwrap_or(default_type);
1067
+ let tgt = qname_to_type
1068
+ .get(&edge.parent_name)
1069
+ .copied()
1070
+ .unwrap_or(default_type);
1071
+ by_pair.entry((src, tgt)).or_default().push(edge);
1072
+ }
1073
+
1074
+ for ((src, tgt), edges) in by_pair {
1075
+ if !graph.has_node_type(src) || !graph.has_node_type(tgt) {
1076
+ continue;
1077
+ }
1078
+ let owned: Vec<super::type_edges::ExtendsEdge> = edges
1079
+ .iter()
1080
+ .map(|e| super::type_edges::ExtendsEdge {
1081
+ child_name: e.child_name.clone(),
1082
+ parent_name: e.parent_name.clone(),
1083
+ })
1084
+ .collect();
1053
1085
  maintain::add_connections(
1054
1086
  graph,
1055
- extends_edges_df(&type_out.extends),
1087
+ extends_edges_df(&owned),
1056
1088
  "EXTENDS".into(),
1057
1089
  src.into(),
1058
1090
  "child_name".into(),
1059
- src.into(),
1091
+ tgt.into(),
1060
1092
  "parent_name".into(),
1061
1093
  None,
1062
1094
  None,