code-graph-rag 0.3.2__py3-none-any.whl → 0.3.13__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.
Files changed (37) hide show
  1. {code_graph_rag-0.3.2.dist-info → code_graph_rag-0.3.13.dist-info}/METADATA +4 -1
  2. {code_graph_rag-0.3.2.dist-info → code_graph_rag-0.3.13.dist-info}/RECORD +37 -32
  3. codebase_rag/cli.py +13 -0
  4. codebase_rag/cli_help.py +39 -0
  5. codebase_rag/constants/__init__.py +1 -0
  6. codebase_rag/constants/ast_rust.py +8 -0
  7. codebase_rag/constants/languages.py +1 -1
  8. codebase_rag/constants/mcp.py +6 -0
  9. codebase_rag/constants/port.py +88 -0
  10. codebase_rag/editing/rename.py +29 -13
  11. codebase_rag/editing/sites.py +5 -3
  12. codebase_rag/graph_updater.py +126 -55
  13. codebase_rag/logs.py +5 -0
  14. codebase_rag/mcp/tools.py +35 -4
  15. codebase_rag/parsers/call_processor.py +64 -3
  16. codebase_rag/parsers/cpp/preproc_recovery.py +5 -0
  17. codebase_rag/parsers/definition_processor.py +64 -0
  18. codebase_rag/parsers/export_detection.py +5 -0
  19. codebase_rag/parsers/factory.py +1 -0
  20. codebase_rag/parsers/rs/type_inference.py +123 -4
  21. codebase_rag/parsers/rs/utils.py +11 -3
  22. codebase_rag/parsers/sql_ddl.py +267 -0
  23. codebase_rag/parsers/sql_recovery.py +343 -0
  24. codebase_rag/parsers/type_inference.py +6 -2
  25. codebase_rag/parsers/utils.py +49 -0
  26. codebase_rag/port_cli.py +97 -0
  27. codebase_rag/port_parity.py +715 -0
  28. codebase_rag/services/__init__.py +9 -0
  29. codebase_rag/services/protobuf_service.py +17 -0
  30. codebase_rag/structural_delta.py +13 -3
  31. codec/schema.proto +82 -0
  32. codec/schema_pb2.py +44 -44
  33. codec/schema_pb2.pyi +144 -20
  34. {code_graph_rag-0.3.2.dist-info → code_graph_rag-0.3.13.dist-info}/WHEEL +0 -0
  35. {code_graph_rag-0.3.2.dist-info → code_graph_rag-0.3.13.dist-info}/entry_points.txt +0 -0
  36. {code_graph_rag-0.3.2.dist-info → code_graph_rag-0.3.13.dist-info}/licenses/LICENSE +0 -0
  37. {code_graph_rag-0.3.2.dist-info → code_graph_rag-0.3.13.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-graph-rag
3
- Version: 0.3.2
3
+ Version: 0.3.13
4
4
  Summary: The ultimate RAG for your monorepo. Query, understand, and edit multi-language codebases with the power of AI and knowledge graphs
5
5
  License-Expression: MIT
6
6
  Keywords: rag,retrieval-augmented-generation,knowledge-graph,code-analysis,tree-sitter,mcp,mcp-server,llm,graph-database,semantic-search,codebase,memgraph,developer-tools,monorepo
@@ -38,6 +38,7 @@ Requires-Dist: defusedxml>=0.7.1
38
38
  Requires-Dist: huggingface-hub[hf-xet]>=1.7.2
39
39
  Requires-Dist: pathspec>=1.0.4
40
40
  Requires-Dist: pygments>=2.20.0
41
+ Requires-Dist: certifi>=2026.10.23
41
42
  Requires-Dist: regex>=2024.11.6
42
43
  Requires-Dist: cgr-graph-rs<0.2,>=0.1.0; platform_python_implementation == "CPython" and ((sys_platform == "linux" and (platform_machine == "x86_64" or platform_machine == "aarch64")) or sys_platform == "darwin" or (sys_platform == "win32" and platform_machine == "AMD64"))
43
44
  Provides-Extra: test
@@ -78,11 +79,13 @@ Requires-Dist: tree-sitter-sql>=0.3.11; extra == "treesitter-full"
78
79
  Requires-Dist: tree-sitter-markdown>=0.5.1; extra == "treesitter-full"
79
80
  Provides-Extra: semantic
80
81
  Requires-Dist: qdrant-client>=1.9.0; extra == "semantic"
82
+ Requires-Dist: grpcio>=1.84.1; extra == "semantic"
81
83
  Requires-Dist: sqlite-vec>=0.1.9; ((sys_platform == "linux" and (platform_machine == "x86_64" or platform_machine == "aarch64")) or sys_platform == "darwin" or (sys_platform == "win32" and platform_machine == "AMD64")) and extra == "semantic"
82
84
  Requires-Dist: torch>=2.6.0; extra == "semantic"
83
85
  Requires-Dist: transformers>=5.0.0; extra == "semantic"
84
86
  Provides-Extra: milvus
85
87
  Requires-Dist: pymilvus[milvus-lite]>=3.0.0; extra == "milvus"
88
+ Requires-Dist: grpcio>=1.84.1; extra == "milvus"
86
89
  Provides-Extra: sqlite-vec
87
90
  Requires-Dist: sqlite-vec>=0.1.9; extra == "sqlite-vec"
88
91
  Provides-Extra: ast-grep
@@ -1,13 +1,13 @@
1
1
  cgr/__init__.py,sha256=S-bP03ZhxMITllHtkg4jA9l28BedNKrM7h8-g_ZrbxE,400
2
- code_graph_rag-0.3.2.dist-info/licenses/LICENSE,sha256=sMS8isnfZtnQqdIEr0i1y6Ln1GnC_2q1FRLeXjExmWM,1071
2
+ code_graph_rag-0.3.13.dist-info/licenses/LICENSE,sha256=sMS8isnfZtnQqdIEr0i1y6Ln1GnC_2q1FRLeXjExmWM,1071
3
3
  codebase_rag/__init__.py,sha256=c5XIoY8eYnFz7hYTojW_-8ZxVQp2Krrfl3ouYKvmfc0,149
4
4
  codebase_rag/_cli_env.py,sha256=_S-3rpEHu7NgjdEmeDVq_dtyDt3lvA_hhrzf1J_CHCg,1007
5
5
  codebase_rag/ast_cache.py,sha256=8q7S8DP1CRd5nzh65StbMPmUGG1mEx-tvKXXSYO4Avg,4204
6
6
  codebase_rag/capture.py,sha256=zRqeLyYAvTd6daDhGlxLJHzJmKjhRyPHPih-9vkDC4g,7494
7
7
  codebase_rag/check_isolation.py,sha256=-WlwMnobSyj84WQ2kKvIEkmspR-2IRtdB62V7Tgt48M,18440
8
8
  codebase_rag/checkout_state.py,sha256=U0dPH6T_TNDOvcFNv7zqfc5CNCwSpM9_Jvjg3S1xnd8,26329
9
- codebase_rag/cli.py,sha256=CYMP6nHino0wiaOwLslUymk5EmxyMvUgWfkQ3BUWjDs,127379
10
- codebase_rag/cli_help.py,sha256=6GbGzw3PjceKLNhjRbXcpear0htu6J4eEN7zhpMttSg,31614
9
+ codebase_rag/cli.py,sha256=yVeD6Z6ZEel86SQ9eSMig6f8LDewy4Z4jSMh_6POs6U,127712
10
+ codebase_rag/cli_help.py,sha256=6JppISRLaewKMxEYkm96iH6XNjH4-9g0go3aHBjlE9w,33502
11
11
  codebase_rag/cli_runtime.py,sha256=xNmo8dIaBRozoIwp5gB0b48RmLt5k_s-Qr_OW5H-Fds,1200
12
12
  codebase_rag/config.py,sha256=_rWN0SjsZI4TkZSJ-5VXfyPAPK9Fs81tqVvQMQPIdwQ,39804
13
13
  codebase_rag/console_marks.py,sha256=Ksd0tJqr5XTYWaKElG-yFnnjNtmqZwJOcNp3S65fups,2479
@@ -32,10 +32,10 @@ codebase_rag/graph_cli.py,sha256=uu-ZdnQse_QZ20MacqDFe5delprJe0yoA6FwoEj14ZE,875
32
32
  codebase_rag/graph_dialects.py,sha256=EM5j9lRyq8-Cw8djcgXzyfXfKT3d8AgbgnQCMXuCB_Q,9697
33
33
  codebase_rag/graph_loader.py,sha256=jHRfcBkDxB2sIU_FYv7NWxIQfC2VwIb-pVlvAmM-2G0,9246
34
34
  codebase_rag/graph_query.py,sha256=L1QxG5XmHiEtwtPm_R1ZMOxj_j5-ITO6wC_ZQqqVAY0,55904
35
- codebase_rag/graph_updater.py,sha256=cpnU3isFIpueTtFgye1q6Lz5W5yJaUaJZB5H2bjluP8,501066
35
+ codebase_rag/graph_updater.py,sha256=vAOche2H6cRniDEI02njuymJwNtqh5v-svKE6zoCny4,505300
36
36
  codebase_rag/interface_skip.py,sha256=jQhV8XarSwWBN0eXplZym8UE4O4qFxdrORSMiwGL2zY,5117
37
37
  codebase_rag/language_spec.py,sha256=jdp-W4kOqEjkIO51Yyps2OeoOShlYDDlarTHPlHd5yA,44027
38
- codebase_rag/logs.py,sha256=9eWPut1eokTNurFxithLlJSARSKXKXI6RGP6URUK_DI,76843
38
+ codebase_rag/logs.py,sha256=0G9u32CAf7BkqvyPjQA1zGL7HTrKsI5rPjAGOctFQp0,77052
39
39
  codebase_rag/main.py,sha256=V4hoT3SMUS-fV_ynWC0RQkhN0kBqPKRc1pAVfw9C4I8,80895
40
40
  codebase_rag/mgclient_compat.py,sha256=f9m96uhCI5_YB8OXn_LVrSWi1bWLLvpqgYP71PPUKrQ,1891
41
41
  codebase_rag/mgclient_probe.py,sha256=iPElSPAOPsIKmi1DaheyedDepV-YYzbZXp6db4ETsdw,3017
@@ -43,6 +43,8 @@ codebase_rag/models.py,sha256=XuOJ4snQEKlbpq0pn23eBSMxnqqNSHNsX-ft_LiQQTY,4079
43
43
  codebase_rag/parser_fingerprint.py,sha256=LdfT9HtN5Rzzp5mG7C0aF09Dq-0MJ7cV4tCiLdqiQ0U,24189
44
44
  codebase_rag/parser_loader.py,sha256=5yPCQQaoDl27YFCjqWnMI7wXqyUlbbnUqGcqUE2dODw,20098
45
45
  codebase_rag/path_filters.py,sha256=Bb623uxr9eBMuB-RKpJ8mGAyuGwqlWaLrJ3H5EnSV5Y,2657
46
+ codebase_rag/port_cli.py,sha256=nYxWVpwCfSnW4abVvRbHytO3-n2TTLIcPke-yn1-v84,3244
47
+ codebase_rag/port_parity.py,sha256=R9oeoAwAWKhDozABqUYfFyY-i0aXyzTE9hW5ByzsBPw,24847
46
48
  codebase_rag/project_selection.py,sha256=8q1OJ8aykNtmj0EcsR_RX6qnU5bW9e9NyyT88Sv-qq8,4295
47
49
  codebase_rag/prompts.py,sha256=xtEjxv2o_CjF5_gosPClJALUy83yKOmwe4BnoaYLNiM,38651
48
50
  codebase_rag/readme_sections.py,sha256=V1ij2u108eisYWLXeGscEnWOjcMeeLG3naXm8mC_S30,18226
@@ -52,7 +54,7 @@ codebase_rag/schemas.py,sha256=iUpz4pBSSOnq1-j0ywAgvSgUlPMpbf5I-ZMzX80kW44,2575
52
54
  codebase_rag/sql_names.py,sha256=dhwz44KsiaZD-XzNlaj4VY9x4BKuR51IYKxAh_Yo5NA,2904
53
55
  codebase_rag/sqlite_vectors.py,sha256=7rb4ahHD40VzlQ2F3Yo-fikP-jfrcysmeDoHeTqqF7Y,6838
54
56
  codebase_rag/structural_check.py,sha256=h1jw4716kz13U9rJp5lsf4QuP5o3lSr6yuGO7jdN3po,20489
55
- codebase_rag/structural_delta.py,sha256=2HRi4ZWWVIDod_FZr7h9-WU-ndjSQDkrl4oJc0mR-Eg,103139
57
+ codebase_rag/structural_delta.py,sha256=e0ubbwoHUI9BbfaidGFu9yw2NCOPSu5SfGXnEXJgPVQ,103547
56
58
  codebase_rag/sync_guard.py,sha256=MZBHF1d6CiJHCbiuASNN8BcS7aJ6wxO3JsI6CsNXGYI,10144
57
59
  codebase_rag/sync_lock.py,sha256=HK_pZ5TSHJuLTEG-EoAGlMZhPkzPgsnne6rEIK0V628,17284
58
60
  codebase_rag/taint.py,sha256=9TmoDQmPyVoK1OJSqG9elSTnwp8RM96kgHPtCMPTk-I,5112
@@ -109,7 +111,7 @@ codebase_rag/analyzers/ast_grep_rules/smells/rust.yaml,sha256=qiKW1JOB5052oBqMBM
109
111
  codebase_rag/analyzers/ast_grep_rules/smells/scala.yaml,sha256=sVr4k0_swfdKe2KyARVPbehx03iZyX6mLmIjU5Nt5qI,695
110
112
  codebase_rag/analyzers/ast_grep_rules/smells/tsx.yaml,sha256=KEQXJSsvG1fG3POUQjgOwXH6HijaCKcC_9sxanRFzyo,588
111
113
  codebase_rag/analyzers/ast_grep_rules/smells/typescript.yaml,sha256=lE9a9253EssgLrqtEBFdNnxQXgc2WRV9VW6pR2v7QrM,613
112
- codebase_rag/constants/__init__.py,sha256=4s-mJa9I-JX4Axr4iIdtIPte-e0c1UpBrmYjEuVD2A0,1466
114
+ codebase_rag/constants/__init__.py,sha256=CbirpXQHuBe2Gaa-f6eY6p1oPkn5LRmroqYZvlBTJds,1500
113
115
  codebase_rag/constants/ast_cpp.py,sha256=ywq08WofG7NXczJFfBidBE6PYRtVXMEVtTgf5yzqsQk,23670
114
116
  codebase_rag/constants/ast_csharp.py,sha256=AETGcDbb0MHdrz35VVmDBDTAHku-mqk14KDrFa_w7AI,20658
115
117
  codebase_rag/constants/ast_dart.py,sha256=b4yUEQ1DOcklFEko9qhJVPxGbESXUyfwJGuWIh2ABVM,13231
@@ -121,7 +123,7 @@ codebase_rag/constants/ast_lua.py,sha256=o3QvV40pmlEIVPfUxbWKXNDaxRBDsDxT4YXL81d
121
123
  codebase_rag/constants/ast_nodes.py,sha256=29eVsZcfNSaBOhgMuKL0KaduODK5Fj0oBQ0ZAo8WG0E,19884
122
124
  codebase_rag/constants/ast_php.py,sha256=1l3xvpdtBGXtelG2OpdASVKv6ek3EdmYPPuTPT6w2x8,5920
123
125
  codebase_rag/constants/ast_python.py,sha256=oLmv-Uh9mXZoBWKFcbpj7sV3JBjcpWr2YI0Q0s40dto,18618
124
- codebase_rag/constants/ast_rust.py,sha256=pXMQS_H9Zlq7C2-zOhSwj763Bvmxk5w6TTDthS8LTok,18652
126
+ codebase_rag/constants/ast_rust.py,sha256=1bR7UbWBqA7ek_kDltJLYTbMhQAMhcm5QlTYgbJ4tro,18972
125
127
  codebase_rag/constants/ast_scala.py,sha256=UH_J3jMhdLYnrU5cUULQzgE00oF0AiEr7FzIYm49IM4,5360
126
128
  codebase_rag/constants/ast_sql.py,sha256=83z3itc0dnJRi-uF07fmD6J7LZNRFlQO1DCYelp3x50,956
127
129
  codebase_rag/constants/build.py,sha256=2CEok0bhBdXD8nf0qJHi8nTYLol5q8It9sz6dJnGNC0,3658
@@ -136,9 +138,10 @@ codebase_rag/constants/graph.py,sha256=du3dUZIZW5MQw5RNzj4EtGWpYwKLiDxeER5LUtQ5u
136
138
  codebase_rag/constants/health.py,sha256=LCev7fiqO4GIy5jTIHdjjUg809tXu5QZbA9OMk1GsqA,6305
137
139
  codebase_rag/constants/interface.py,sha256=4K3F4lGeDohwfG5sGLi1_n2wPl5-BqDKNS3_bKeDcZk,6909
138
140
  codebase_rag/constants/lang_tooling.py,sha256=MazwX4qqUSUgv-WIZP7LTDp514BZzHu48CVedbnACJU,12004
139
- codebase_rag/constants/languages.py,sha256=_Oxxgl7Q5RxEA_QN7I2g1ATXx53DIdjUdB0nOwyo_OE,20540
140
- codebase_rag/constants/mcp.py,sha256=i77DSjqEIpWfZnW-dufpSiUrSOS8n-UdaSO1shG98Go,14100
141
+ codebase_rag/constants/languages.py,sha256=Q5QSJuPtrb67SAgoFMzWvt9tG0cNlLhm1bHVwDLxBIs,20748
142
+ codebase_rag/constants/mcp.py,sha256=SBwYMphmnO_o6U-NBjPfwDzl0WGhlVbI_t84F1jsOgE,14382
141
143
  codebase_rag/constants/notebook.py,sha256=BlKxgAXG6a2ahIVxUtOj79sXNw6sNpXRQ-9hAGHfUho,2288
144
+ codebase_rag/constants/port.py,sha256=Xv17igW6ojSXNm_UoX0lf9z8V9XOMwIn6eUYlvIUiHE,3278
142
145
  codebase_rag/constants/providers.py,sha256=wF9by3FY42AkETMDxczJfrxshprxtbENbl4z1crDPo0,7957
143
146
  codebase_rag/constants/security.py,sha256=9iePndqz7jCVivSxshK7_bx6KzdPd_7fcOPlUDFGVVc,52025
144
147
  codebase_rag/constants/sqlite_vectors.py,sha256=OgzxlQ327olQUiksJQdF6lufyQRnWC4ZvLV2ZRky0kc,3949
@@ -152,11 +155,11 @@ codebase_rag/editing/import_paths.py,sha256=QQ91cJmHTMWGj-XExzk3mvg_1_a2eWHrvRKB
152
155
  codebase_rag/editing/imports.py,sha256=hIlFDXUvVFNQphbJ7p5yEGtaOgJLtAIaq2D0FueVGu0,23818
153
156
  codebase_rag/editing/occurrences.py,sha256=q26BLQRi9OCleye5g-pBzRRPitn7IZF3VW_ho7rgT34,57551
154
157
  codebase_rag/editing/patcher.py,sha256=dayvXagkkY0IoLQ49GhmpFp1T96ZLQR50gaY8eJlfyA,18724
155
- codebase_rag/editing/rename.py,sha256=uk0WJ9ydcqzdRTp-O5f2iLwlAnlKj7ZycJ5If2q4jnM,98058
158
+ codebase_rag/editing/rename.py,sha256=V18VomGs4Ci4ZmWayM5IYJD01fzVBou4pczjpyXCPvk,98748
156
159
  codebase_rag/editing/signature_bind.py,sha256=cCEnW5OkYkSceMMYUbd3sJXrKETyCFzryraIMcId7Ek,22070
157
160
  codebase_rag/editing/signature_contract.py,sha256=bZDoTGZWB8q7zoUga3VGSEt13XFI1aqO6gWFWGn9Wh8,4385
158
161
  codebase_rag/editing/signature_spec.py,sha256=X5Exea4MP_JH3Q3VOpey9qDdhpCIPDZebCK_Gh3xJhU,11518
159
- codebase_rag/editing/sites.py,sha256=DjuwM8Zp5hjM4q2TaOodyrxE-B4CYQ2uuSjv7lZDZKI,4849
162
+ codebase_rag/editing/sites.py,sha256=YHtja03C5vBeIRlbpr6RdDMtsrkm2abAaQwDhE_Bs-s,5074
160
163
  codebase_rag/editing/transaction.py,sha256=2IvbDjXlCnIBnbhBbTAH02E-0PQODbjCs0aTiUvUcok,23413
161
164
  codebase_rag/embedded_graph/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
162
165
  codebase_rag/embedded_graph/compiler.py,sha256=W2JTn6zcm12v2JsovPYcrsOPQFK-2VMC74k9HgXT6h0,30345
@@ -177,19 +180,19 @@ codebase_rag/embedded_graph/types.py,sha256=PpaH7a2SjxRZcW-snxvtfdGo8PzEcF6iACN8
177
180
  codebase_rag/mcp/__init__.py,sha256=juk_YZdWiNWWI05Z_yMLvF-6Bb4DRha2sF5bAfxQGy0,124
178
181
  codebase_rag/mcp/client.py,sha256=YP1Ij_tNRj1q-wcaC5iu0DeHBmYn9T-r3pC-fnFTdrk,2129
179
182
  codebase_rag/mcp/server.py,sha256=Lp6GteZN1s1ompS3bpmYZeBz6X2XzSL2KM5rBSkxqro,13252
180
- codebase_rag/mcp/tools.py,sha256=4VUBbMZvYKLQUSZrHzrZ_W9eCrWnM_Ljlxo5dLCV4PA,186265
183
+ codebase_rag/mcp/tools.py,sha256=A9Am4ZiMzqfLTZ4zdOjEtYLsShLJc1p5K6pm1luYGJA,187656
181
184
  codebase_rag/parsers/__init__.py,sha256=xtnU-zqOEq6kxRuHfFI7kJOB2c1fcfbe92AXoc2LZnU,512
182
185
  codebase_rag/parsers/anchor_hash.py,sha256=j06i0wuHw1IW-zOAgPLeQcRZ3AM3FDA6fS0ayIxKGhA,3933
183
186
  codebase_rag/parsers/ast_fingerprint.py,sha256=ebmDDZWumWg_2Xiwm-cxLqAc8_Fs6PK1N7-y8jfgP4Y,6817
184
187
  codebase_rag/parsers/ast_grep_scope.py,sha256=_I9OyrshBJt5yJyS6oSlJcZJtV4lc4tbofnAJUJCvJg,9090
185
188
  codebase_rag/parsers/ast_grep_tier.py,sha256=7ZN4lOKaYMT-1AKa9tMd9jR2QZ0CS12Fr6yd0aCoDsQ,24812
186
189
  codebase_rag/parsers/build_lock.py,sha256=vS8r8IQnf9VFC1KXFLDedMHC5E1qopM8RSocuhxV_dM,7374
187
- codebase_rag/parsers/call_processor.py,sha256=mBli6vwZ6B-DJrOD7kIfJ1edsCZo0fcKwQCXlTt1U0o,573482
190
+ codebase_rag/parsers/call_processor.py,sha256=6QdxfV3bpB1y-vbh-uyXS0t4MJafzk18y1FMVqRLMqc,576428
188
191
  codebase_rag/parsers/call_resolver.py,sha256=4BDy9MrxYPS7Tr0oBQ_sXWq9Hm_C9THsSZLsw98nAUo,360325
189
192
  codebase_rag/parsers/contract_linking.py,sha256=ezc1OC0q-MW-5dWzrTMNROfapkElsXcfaXeZmtfO5Ao,10607
190
193
  codebase_rag/parsers/contracts.py,sha256=yu_Y_AVZ-RqqA_6p4d_ZoU326lQzTT7rJFsu-VK3AfI,10637
191
194
  codebase_rag/parsers/definition_docstring.py,sha256=4ZC1_AzBq2QrGHmxrfbk3Avrix6AecEa_j60tCsLc9Y,18755
192
- codebase_rag/parsers/definition_processor.py,sha256=1TFAL5vEO2R0Kak6myIPI32dHUixFTCZkZ7vGn_lksA,54964
195
+ codebase_rag/parsers/definition_processor.py,sha256=Ntt0wY5PvAoU5dGhyJsojutFnnaZr_mlRpP8FhdcSuc,57710
193
196
  codebase_rag/parsers/dependency_parser.py,sha256=XFfhqQLO_ThU_6-8PYtzPq_K_nWyPGOuKrVKjmf7WrA,13864
194
197
  codebase_rag/parsers/dispatch_registry.py,sha256=rVqPbLEwvaKT13TQkx5RooVhNvi43OMpS1SnW6Kx7EY,15641
195
198
  codebase_rag/parsers/document_tier.py,sha256=ZPyuTQOmaoYwLtrIF_NAHjijdnRWIU0uN-oVPXs5jAM,57313
@@ -197,8 +200,8 @@ codebase_rag/parsers/endpoint_prefixes.py,sha256=ngN_9m4yLOfXn3e20nM2_cRtthI_rLD
197
200
  codebase_rag/parsers/endpoint_routes.py,sha256=JZ94c8cTcW3O3QM8y0lfyfmOmDazmS5ruXaaKLUWZ9s,37623
198
201
  codebase_rag/parsers/endpoints.py,sha256=L_EGZK_x7UmKB9LndWlhOtLgpKAveqOpF8ccKoSoa1k,25680
199
202
  codebase_rag/parsers/enum_variants.py,sha256=-9RotgSmsyBwF4PaloX-Uk8PxO9GPtMSgLu5JxbLgKU,16323
200
- codebase_rag/parsers/export_detection.py,sha256=mn7KwmbfYeB92J-2SoayywMugKrUYzDM6Yn0qaAU2Os,36388
201
- codebase_rag/parsers/factory.py,sha256=UD5Vj2rbN4BCPxIVLLkDNPkAWYaGvuHPXptNwHc2QXk,11106
203
+ codebase_rag/parsers/export_detection.py,sha256=wo7CrrVGRD1oAGBOdr_C1otGSOVCfZJGHaEGQeb7rC8,36628
204
+ codebase_rag/parsers/factory.py,sha256=rtLqC7J5X4XLPFw79f0d8EnrWF_OqUdFwgpAFP8huDM,11173
202
205
  codebase_rag/parsers/field_nodes.py,sha256=z09guAcAGXAtwZ5IIFs0tHDRqXLfD-krYJo0Nom8kts,40237
203
206
  codebase_rag/parsers/flat_module.py,sha256=Fk6s3C3OscwMDhVfWUM5zmsIWhZOT6hzs7j7zA0K-20,4239
204
207
  codebase_rag/parsers/frontend_mode.py,sha256=p7NZ6J0O9D_-CmDmtbRpz8__mnO7bJ816qpiajA-3AE,1348
@@ -214,12 +217,14 @@ codebase_rag/parsers/positional_params.py,sha256=iirZhgmeyOfKCUgcxDLuHb_xRjIKIgY
214
217
  codebase_rag/parsers/python_source_roots.py,sha256=T0ediMgLGxJwP1H2OIjp6CMbMnvKxMZ2pINaQhLiacY,8885
215
218
  codebase_rag/parsers/rpc_exposure.py,sha256=DfbNqENBBuoAGdRAUpXyMiUVx1q4Ws1jLGDDXb3uIqs,25424
216
219
  codebase_rag/parsers/semantic_call_join.py,sha256=1ETRfMMpdEKKTz4nMFpm7LPprLJQcBGYIUlZwx1oqL8,4339
220
+ codebase_rag/parsers/sql_ddl.py,sha256=7h4RtCjTMbSaxlK8ONXbL2c1PIle_sU6GTdAOQEUiJ4,10153
221
+ codebase_rag/parsers/sql_recovery.py,sha256=DbK--2wG90cvayDJbwt4BMt4HzJKI94gq4hs1q8iRqM,12826
217
222
  codebase_rag/parsers/stdlib_extractor.py,sha256=8WKASdCxT0eiNxebjo3o61YuhH9sqgXXxXSn5K6LhiM,25197
218
223
  codebase_rag/parsers/string_call.py,sha256=LW0eN1Pr-QFEch92T3gITSJXXFhZeHE-T2423w-AkiY,9152
219
224
  codebase_rag/parsers/structure_processor.py,sha256=1Cb8KSccFPJRNbiFEXioXZYY6lOphuZok7QYYJlmokk,11607
220
225
  codebase_rag/parsers/type_facts.py,sha256=PKTgQSL6lcSTyOawGlp33YbfdPyeMy5ITHcEJ_iqJao,22755
221
- codebase_rag/parsers/type_inference.py,sha256=ZPqBVa01BhDq8N4DVZNXFMYt6Eo1_JMLVsk6A_O_VXg,62804
222
- codebase_rag/parsers/utils.py,sha256=ISC-UQow9edxiiaeRhyi8_CvkLKYSBNG3j4nr4z_c2Q,93437
226
+ codebase_rag/parsers/type_inference.py,sha256=f528JhSq2rWHXXBtq7SlG_NPbjQmcbBCvQO_g8Nn2Mw,63024
227
+ codebase_rag/parsers/utils.py,sha256=cteorza2jQLME2FF_MtzvwUmyultShk1eUAJP5kkYmY,95331
223
228
  codebase_rag/parsers/ast_grep_patterns/bash.yaml,sha256=zImcF0Lp249J0hP3Kt8NI53E0JdvFp_MQX2gyoH_-AQ,308
224
229
  codebase_rag/parsers/ast_grep_patterns/elixir.yaml,sha256=apLOGind4n0rBcYgK-619iHibUPsJL5jXGpQuJwvMQA,1079
225
230
  codebase_rag/parsers/ast_grep_patterns/haskell.yaml,sha256=2b5uZZFH_QbrtV-Wkt866MMw7YALCAdNpXJeOMu6U8A,807
@@ -243,7 +248,7 @@ codebase_rag/parsers/cpp/class_macro.py,sha256=iMLemLqZYm9Gikf299hgsE8yloyt965WN
243
248
  codebase_rag/parsers/cpp/function_values.py,sha256=09HrwrffFopl-9Cql7OwDF5xs7r828ONKXLTCawJy1U,7420
244
249
  codebase_rag/parsers/cpp/local_types.py,sha256=2ZeQWQDV7yjsEppADRytDTxhXshQ5GRxFqgiXVgjiQM,18278
245
250
  codebase_rag/parsers/cpp/overloads.py,sha256=sG447ThxdJ5-GtwiqmIOHYiXAyIy81-G4yCjoVQHrxE,9390
246
- codebase_rag/parsers/cpp/preproc_recovery.py,sha256=M4h2bQyfm18b21aTB-7lDAPBuTVsgJeuTGngLUdYiEc,18973
251
+ codebase_rag/parsers/cpp/preproc_recovery.py,sha256=NRxsFJbs680tOzzGJHIyckC4ANnJZ2b4yOS6mPX4sng,19246
247
252
  codebase_rag/parsers/cpp/trailing_annotation.py,sha256=TgO6Pjax3R-U8DclaJo-JwWZtiHpk_QTzSC2FgKiqH8,11809
248
253
  codebase_rag/parsers/cpp/type_inference.py,sha256=jFC9fnBJgpnvIF1Kx-FORbH5fXsj27ACvW4hpDEI1dQ,25482
249
254
  codebase_rag/parsers/cpp/utils.py,sha256=AHI9jZSD2OCRy4Co_WK6PmcyzH6qu9B6vRR0pKcxn5Y,37819
@@ -351,8 +356,8 @@ codebase_rag/parsers/py_frontend/__init__.py,sha256=7MKmgZ0cgMSDK8jaTSJgYP02vby3
351
356
  codebase_rag/parsers/py_frontend/frontend.py,sha256=q-PFwpeevmTCcCX8OJA0p1uaRa4c6_qBA1ookNVyToI,8453
352
357
  codebase_rag/parsers/rs/__init__.py,sha256=gpDaC5pcpBrtVpXWU-0zkG1iXZSTSs7uHtEKXFDCmQs,91
353
358
  codebase_rag/parsers/rs/cargo_semver.py,sha256=yg_jBYiJRwslpIpbs4WyXNnrc_L_i00K8QgMpzlfKBc,8312
354
- codebase_rag/parsers/rs/type_inference.py,sha256=xZUZ-PN4041xj_Z2sT8PP7QSgKfxbJlsdiuUVlpVvBY,40382
355
- codebase_rag/parsers/rs/utils.py,sha256=2igzLUqIMUQBWuFiqqGbzfc9vC3hKwkr4NDAfFAX6k4,34439
359
+ codebase_rag/parsers/rs/type_inference.py,sha256=c9_XzUbE5gvNOrs4dPC0fNAeioEmIPshsvjzYsKcSys,45933
360
+ codebase_rag/parsers/rs/utils.py,sha256=p4kjxh342AbSqnSGwJjqdKZuPRL57WpM1u8BceaLsas,34927
356
361
  codebase_rag/parsers/scala/__init__.py,sha256=Opt-JbErgor6clOzuawQqxWTlp-VN2IGWtIBP-EYOhA,712
357
362
  codebase_rag/parsers/scala/utils.py,sha256=HQ-rdZeejD04ex7cNw2Mme4HbsLH6CKuvBgyUsSTZ-g,21501
358
363
  codebase_rag/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -367,7 +372,7 @@ codebase_rag/queries/highlights/php.scm,sha256=plLTCNRWU-8LDCInfXIOGp3wdxNYGGhwb
367
372
  codebase_rag/queries/highlights/python.scm,sha256=GBWM0NibORqEzhIjoBZVbFvVx3UPBNu3x60IXJA1TII,65
368
373
  codebase_rag/queries/highlights/rust.scm,sha256=sI4A8LXuaRoXBIT9Rr7b27UvX2fMgwQ05WTumBR7zco,165
369
374
  codebase_rag/queries/highlights/typescript.scm,sha256=RM3VfWa8rOBzqjXr6yUguQw_vTC3ccidahrGuMwvNhE,203
370
- codebase_rag/services/__init__.py,sha256=SkntffIneo0kIcrframBOMW-nEfgPi5uyL_VlY523vw,1392
375
+ codebase_rag/services/__init__.py,sha256=pXegiIMO3HnQzdgl-pAqcie5gIepLnDNbx82VS1pAm8,1709
371
376
  codebase_rag/services/anthropic_token_counter.py,sha256=vPN73PzqZLNQ6VtGnv8ALNf1gtRp_nJFdd47zr-iZMc,6575
372
377
  codebase_rag/services/cypher_guard.py,sha256=SdgyXl6MR8a0HzV_Nb7g-zbCtn5GP3wnhu-J2bHm-a8,7433
373
378
  codebase_rag/services/filtering.py,sha256=AjzXfTX_iL2ypPILkVJeTac8UOCcwrMNsk_fYbL1iiY,2137
@@ -376,7 +381,7 @@ codebase_rag/services/graph_diff.py,sha256=TUn_rin1XXYUs-VNoksGI7nZSa7mWnLaL8pLW
376
381
  codebase_rag/services/graph_service.py,sha256=n3kmA3mSan2h23NcYRoQAtoMRFe02-gqyugX-jSMwv8,48483
377
382
  codebase_rag/services/llm.py,sha256=LrDQcpet4EXYV9vkCmIftvTsB4UAtc0RB5GCmEis7DY,14181
378
383
  codebase_rag/services/neo4j_driver.py,sha256=RtEQ4t8QhmeCxPBJ8wwUDn014SA2Tye2hgtpp1mFRUI,10305
379
- codebase_rag/services/protobuf_service.py,sha256=ixuex9BDN8-1e8jfIUgNKL3tfkqAZk-oYuBjrQws_gY,13150
384
+ codebase_rag/services/protobuf_service.py,sha256=yOWr41kT81mF7617gV5G84Iz-rMte2O8rfIRFaWeDhQ,14008
380
385
  codebase_rag/services/provenance.py,sha256=cYQ-WI6Gp-Ugdrpvo7wI4Ca93_Kf1xHlGmyMZiE7C6E,11237
381
386
  codebase_rag/services/resource_cleanup.py,sha256=aIS8id9PZOMbCUNRDGvD1ePjSf_2TkPhlmQux6J_QcU,1831
382
387
  codebase_rag/services/usage_cost.py,sha256=lsUQezBzQoaL6O0j5asWOXfeC_7zojWIV-_o_ThAaTw,1306
@@ -444,11 +449,11 @@ codebase_rag/workspaces/constants.py,sha256=6Hi5EYQTgfXYT_N84_V_JEdqlg9qfqJTTSoU
444
449
  codebase_rag/workspaces/models.py,sha256=tT-5HtJTpMlCan9y-5ba3ShC-ZHGyl-N8BX8A6nehFY,1139
445
450
  codebase_rag/workspaces/storage.py,sha256=jTLaCM39szKF--jxfomQmrC8Nd3NaI3Yj7raaxEBgZw,5087
446
451
  codec/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
447
- codec/schema.proto,sha256=FhobUEQnBLFEZQ8jmtBWGd2TKI5IOsbRoBKqKahNgTY,18069
448
- codec/schema_pb2.py,sha256=kVvFiQ0rpjBOaJEZuTO9sfmR8UdDkxEkCTeY7r-DpIg,15035
449
- codec/schema_pb2.pyi,sha256=-1fXY9k7N8ggiPxLutGM8F2LzlPoUZBZvP_0YCQfwB4,30683
450
- code_graph_rag-0.3.2.dist-info/METADATA,sha256=A_gciJC-so_cK8CwbJK6mq0Xct_Tng9GuvbMa0QmYpM,17850
451
- code_graph_rag-0.3.2.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
452
- code_graph_rag-0.3.2.dist-info/entry_points.txt,sha256=o_VMDZJNNcBebcFuItBluVpmChH-wikLj5NcvlsI3k8,140
453
- code_graph_rag-0.3.2.dist-info/top_level.txt,sha256=exG1ozDEpugwIqwzcE97Nko_Cat7cd5eAo99lARpulA,23
454
- code_graph_rag-0.3.2.dist-info/RECORD,,
452
+ codec/schema.proto,sha256=EEdmt2YOf0ipC9w0TnfjxcuFeyZKtHckR-xT6fZZ_QY,21228
453
+ codec/schema_pb2.py,sha256=uCnOpBO07TqZK-H4nVrLyii5QCgglcaY1k-SAYlCqWE,17601
454
+ codec/schema_pb2.pyi,sha256=1-rh_zgkeGHOos5zXUSHyiegQDLDlC7f2ZmJqz7qX8U,38450
455
+ code_graph_rag-0.3.13.dist-info/METADATA,sha256=bTBR-s4JQsRAKuyoemvHKV1zU5HDdKTzek2EB259sDs,17986
456
+ code_graph_rag-0.3.13.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
457
+ code_graph_rag-0.3.13.dist-info/entry_points.txt,sha256=o_VMDZJNNcBebcFuItBluVpmChH-wikLj5NcvlsI3k8,140
458
+ code_graph_rag-0.3.13.dist-info/top_level.txt,sha256=exG1ozDEpugwIqwzcE97Nko_Cat7cd5eAo99lARpulA,23
459
+ code_graph_rag-0.3.13.dist-info/RECORD,,
codebase_rag/cli.py CHANGED
@@ -59,6 +59,7 @@ from .editor_links import (
59
59
  from .graph_cli import cli as graph_cli
60
60
  from .graph_dialects import DIALECT_MEMGRAPH
61
61
  from .parser_loader import load_parsers
62
+ from .port_cli import cli as port_cli
62
63
  from .project_selection import ProjectChoice, choose_project, scope_refusal
63
64
  from .services.gloss_cleanup import prune_orphaned_glosses
64
65
  from .services.graph_diff import DiffError, diff_indexes, diff_is_empty
@@ -2176,6 +2177,18 @@ def graph_command(ctx: typer.Context) -> None:
2176
2177
  _run_delegated_group(graph_cli, ctx)
2177
2178
 
2178
2179
 
2180
+ @app.command(
2181
+ name=ch.CLICommandName.PORT,
2182
+ help=ch.CMD_PORT,
2183
+ short_help=ch.CMD_PORT,
2184
+ add_help_option=False,
2185
+ context_settings=_DELEGATED_GROUP_CONTEXT,
2186
+ rich_help_panel=ch.PANEL_USE,
2187
+ )
2188
+ def port_command(ctx: typer.Context) -> None:
2189
+ _run_delegated_group(port_cli, ctx)
2190
+
2191
+
2179
2192
  @app.command(
2180
2193
  name=ch.CLICommandName.CHECK,
2181
2194
  help=ch.CMD_CHECK,
codebase_rag/cli_help.py CHANGED
@@ -23,6 +23,7 @@ class CLICommandName(StrEnum):
23
23
  CHECK = "check"
24
24
  RENAME = "rename"
25
25
  CONTEXT = "context"
26
+ PORT = "port"
26
27
  STOP = "stop"
27
28
  STATUS = "status"
28
29
  HELP = "help"
@@ -206,6 +207,43 @@ EXAMPLES_CONTEXT = (
206
207
  )
207
208
  HELP_CONTEXT_TARGET = "Qualified name, bare name, path:line, or a free-text task."
208
209
  HELP_CONTEXT_BUDGET = "Token budget for the slice."
210
+ CMD_PORT = "Judge a port between languages: pair the functions of two implementations and compare their calls and arity, as JSON, no LLM."
211
+ CMD_PORT_GROUP = CMD_PORT
212
+ CMD_PORT_CHECK = (
213
+ "Pair the functions and methods under OLD_SCOPE with those under "
214
+ "NEW_SCOPE (paths relative to the repository root) and judge each pair "
215
+ "MATCH, DIFFERS or UNKNOWN on the first-party calls it makes and its "
216
+ "positional arity; unpaired and ambiguous definitions are listed."
217
+ )
218
+ EPILOG_PORT = "Run 'cgr help port COMMAND' for command-specific help."
219
+ # `\b` opens a paragraph click prints as written instead of rewrapping it,
220
+ # which keeps one example per line.
221
+ EPILOG_PORT_CHECK = (
222
+ "\b\nExamples:\n cgr port check src/legacy src/rewrite\n"
223
+ " cgr port check codebase_rag/embedded_graph rust/cgr_graph_rs/src "
224
+ "--fail-on-found\n"
225
+ " cgr port check py rs --pair py.lexer._lex=rs.lexer.tokenize\n\n"
226
+ "Exit status: 0 with the JSON report; 1 with --fail-on-found when a pair "
227
+ "DIFFERS; 2 when the scopes overlap or a --pair is malformed or repeated; "
228
+ "3 when a project is not indexed; 4 when a scope holds no function or "
229
+ "method, or a --pair names none or several."
230
+ )
231
+ HELP_PORT_NEW_PROJECT = (
232
+ "Project holding NEW_SCOPE, when the new implementation was indexed as a "
233
+ "project of its own (default: the project of OLD_SCOPE)."
234
+ )
235
+ HELP_PORT_PAIR = (
236
+ "Pair two definitions explicitly, OLD=NEW, each a qualified name or a "
237
+ "dotted suffix naming exactly one definition of its side. Repeatable."
238
+ )
239
+ HELP_PORT_INCLUDE_TESTS = (
240
+ "Pair test functions too; by default they are left out, classified as "
241
+ "tests-reaching classifies them."
242
+ )
243
+ HELP_PORT_FAIL_ON_FOUND = (
244
+ "Exit with status 1 when any pair DIFFERS. UNKNOWN pairs and unpaired "
245
+ "definitions do not fail the check."
246
+ )
209
247
  CMD_TRACE_INGEST = "Resolve a trace file against a project and write dynamic edges"
210
248
  CMD_TRACE_CONVERT = "Convert a V8 .cpuprofile (node --cpu-prof) to a trace file"
211
249
 
@@ -678,6 +716,7 @@ CLI_COMMANDS: dict[CLICommandName, str] = {
678
716
  CLICommandName.CHECK: CMD_CHECK,
679
717
  CLICommandName.RENAME: CMD_RENAME,
680
718
  CLICommandName.CONTEXT: CMD_CONTEXT,
719
+ CLICommandName.PORT: CMD_PORT,
681
720
  CLICommandName.WORKSPACE: CMD_WORKSPACE,
682
721
  CLICommandName.STOP: CMD_STOP,
683
722
  CLICommandName.STATUS: CMD_STATUS,
@@ -30,6 +30,7 @@ from .lang_tooling import * # noqa: F403
30
30
  from .languages import * # noqa: F403
31
31
  from .mcp import * # noqa: F403
32
32
  from .notebook import * # noqa: F403
33
+ from .port import * # noqa: F403
33
34
  from .providers import * # noqa: F403
34
35
  from .security import * # noqa: F403
35
36
  from .sqlite_vectors import * # noqa: F403
@@ -113,6 +113,14 @@ TS_RS_LOOP_EXPRESSION = "loop_expression"
113
113
  # A Rust call node whose callee is descended for chain flattening: a plain call
114
114
  # or a turbofish generic_function (`f::<T>()`).
115
115
  RS_CALL_OR_GENERIC_FN = (TS_RS_CALL_EXPRESSION, TS_GENERIC_FUNCTION)
116
+ TS_RS_GENERIC_TYPE_WITH_TURBOFISH = "generic_type_with_turbofish"
117
+ # A constructor path with type arguments: `Tree::<u8>::Leaf`'s qualifier,
118
+ # `Wrapper::<u8> { .. }`'s name, `Pair::<u8>(..)`'s callee.
119
+ RS_GENERIC_CONSTRUCTOR_PATHS = (
120
+ TS_GENERIC_TYPE,
121
+ TS_RS_GENERIC_TYPE_WITH_TURBOFISH,
122
+ TS_GENERIC_FUNCTION,
123
+ )
116
124
  TS_RS_TUPLE_STRUCT_PATTERN = "tuple_struct_pattern"
117
125
  TS_RS_TYPE_ARGUMENTS = "type_arguments"
118
126
  TS_RS_TRY_EXPRESSION = "try_expression"
@@ -401,7 +401,7 @@ LANGUAGE_METADATA: dict[SupportedLanguage, LanguageMetadata] = {
401
401
  ),
402
402
  SupportedLanguage.SQL: LanguageMetadata(
403
403
  LanguageStatus.DEV,
404
- "Stored functions (CREATE FUNCTION), schema-qualified names, invocations between routines. CREATE PROCEDURE and in-depth PL/pgSQL bodies await upstream grammar support: the published grammar parses plain SQL statements only",
404
+ "Stored functions (CREATE FUNCTION), each one a dead-code root, schema-qualified names, invocations between routines, and the DDL that runs a routine by name (trigger EXECUTE FUNCTION, aggregate SFUNC/FINALFUNC, cast WITH FUNCTION, operator FUNCTION, type INPUT/OUTPUT, HANDLER/VALIDATOR, SUPPORT). CREATE PROCEDURE and in-depth PL/pgSQL bodies await upstream grammar support: the published grammar parses plain SQL statements only",
405
405
  "SQL (PostgreSQL)",
406
406
  ),
407
407
  SupportedLanguage.JULIA: LanguageMetadata(
@@ -246,6 +246,12 @@ MCP_ROOT_NOT_INDEXED = (
246
246
  "No project is indexed for this server's root {path}; run "
247
247
  "index_repository or update_repository first, or pass `project`."
248
248
  )
249
+ # flow_verdict, explain_traceback and rank_root_causes take no `project`, so
250
+ # their refusal cannot advise passing one (issue #3193).
251
+ MCP_FIXED_ROOT_NOT_INDEXED = (
252
+ "No project is indexed for this server's root {path}; run "
253
+ "index_repository or update_repository first."
254
+ )
249
255
  # An empty answer for a name the graph does not hold read as "nothing calls
250
256
  # it" / "no test reaches it" (issue #2461).
251
257
  MCP_UNKNOWN_TARGET = "{qualified_name!r} is not in the graph."
@@ -0,0 +1,88 @@
1
+ # Cross-language parity (`cgr port check`, epic #3413): how two
2
+ # implementations of the same code are paired and judged.
3
+
4
+ from enum import StrEnum
5
+
6
+ from .graph import EdgeResolution, NodeLabel
7
+
8
+
9
+ class ParityVerdict(StrEnum):
10
+ """A dimension's or a pair's verdict. `UNKNOWN` is what the graph cannot
11
+ settle (an edge it only guessed, a signature it did not record), and is
12
+ never reported as `MATCH`."""
13
+
14
+ MATCH = "MATCH"
15
+ DIFFERS = "DIFFERS"
16
+ UNKNOWN = "UNKNOWN"
17
+
18
+
19
+ # A pair's verdict is the worst of its dimensions'.
20
+ PARITY_VERDICT_RANK: dict[ParityVerdict, int] = {
21
+ ParityVerdict.MATCH: 0,
22
+ ParityVerdict.UNKNOWN: 1,
23
+ ParityVerdict.DIFFERS: 2,
24
+ }
25
+
26
+
27
+ class PairedBy(StrEnum):
28
+ """How a pair was found, strongest first: a `--pair` the user gave; the
29
+ same normalised name at the same module path relative to each scope (and
30
+ in the same class, for a method); the same name in the same container
31
+ (the class, or the module file's stem) when the layouts differ; or the
32
+ same name alone with the containers differing, which is the pairing to
33
+ verify."""
34
+
35
+ EXPLICIT = "explicit"
36
+ MODULE = "module"
37
+ CONTAINER = "container"
38
+ NAME = "name"
39
+
40
+
41
+ # The automatic tiers, tried in this order on what the previous one left.
42
+ PORT_PAIRING_TIERS = (PairedBy.MODULE, PairedBy.CONTAINER, PairedBy.NAME)
43
+
44
+
45
+ PORT_CALLABLE_LABELS = frozenset({NodeLabel.FUNCTION.value, NodeLabel.METHOD.value})
46
+
47
+ # Edges the resolver only guessed: a difference resting on them alone is
48
+ # UNKNOWN, since the other side may simply have bound the same call exactly.
49
+ PORT_UNCERTAIN_RESOLUTIONS = frozenset(
50
+ {EdgeResolution.HEURISTIC.value, EdgeResolution.OVERLOAD.value}
51
+ )
52
+
53
+ # Folded before names are compared, so `_read_number`, `readNumber` and
54
+ # `ReadNumber` agree. The boundary pattern splits camelCase and the end of an
55
+ # acronym (`HTTPServer` -> `HTTP_Server`).
56
+ PORT_NAME_STRIP_CHARS = "_"
57
+ PORT_CAMEL_BOUNDARY_PATTERN = r"(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])"
58
+ PORT_NAME_JOIN = "_"
59
+
60
+ # File stems that name a package rather than its contents (`__init__.py`,
61
+ # Rust `mod.rs` and `lib.rs`, `index.ts`): a function's container is then the
62
+ # directory holding the file.
63
+ PORT_PACKAGE_ENTRY_STEMS = frozenset({"__init__", "mod", "lib", "main", "index"})
64
+
65
+ PORT_PAIR_SEPARATOR = "="
66
+ PORT_SCOPE_ROOTS = frozenset({"", "."})
67
+
68
+ # Overlapping scopes and a malformed or repeated `--pair` are usage errors,
69
+ # reported under click's own status for one; a scope or pair naming nothing
70
+ # in the graph exits with GRAPH_EXIT_UNKNOWN_TARGET like `cgr graph`.
71
+ PORT_EXIT_USAGE = 2
72
+ PORT_EXIT_DIFFERS = 1
73
+
74
+ PORT_ERR_EMPTY_SCOPE = (
75
+ "No function or method of project '{project}' lies under '{scope}'. "
76
+ "A scope is a path relative to the repository root, as `cgr graph "
77
+ "definition` prints it."
78
+ )
79
+ PORT_ERR_OVERLAPPING_SCOPES = (
80
+ "The scopes '{old}' and '{new}' overlap in project '{project}': each "
81
+ "definition must belong to one side."
82
+ )
83
+ PORT_ERR_PAIR_FORMAT = "--pair takes OLD=NEW, got '{value}'."
84
+ PORT_ERR_PAIR_TARGET = (
85
+ "--pair: '{target}' names {count} definitions under '{scope}'; give a "
86
+ "qualified name, or a dotted suffix of one, that names exactly one."
87
+ )
88
+ PORT_ERR_PAIR_TWICE = "--pair: '{qualified_name}' is paired more than once."
@@ -471,25 +471,39 @@ def _chain_links(
471
471
  if parser is None:
472
472
  return []
473
473
  root = parser.parse(source).root_node
474
+ # Each link's own site starts at its name (issue #3166); the links of
475
+ # its chain are the calls that start where the site's call starts.
476
+ starts = {call.start_point for call in calls_starting_at(root, line - 1, col)}
477
+ starts = starts or {(line - 1, col)}
474
478
  found: list[tuple[int, int]] = []
475
479
  stack: list[Node] = [root]
476
480
  while stack:
477
481
  node = stack.pop()
478
- if node.start_point == (line - 1, col):
479
- func = node.child_by_field_name(cs.FIELD_FUNCTION)
480
- member = func.children[-1] if func is not None and func.children else None
481
- if (
482
- member is not None
483
- and member.type in _MEMBER_NAME_TYPES
484
- and member.text is not None
485
- and member.text.decode(cs.ENCODING_UTF8, errors="replace") == name
486
- ):
487
- found.append((member.start_point[0] + 1, member.start_point[1]))
488
- if node.start_point[0] <= line - 1 <= node.end_point[0]:
482
+ if (
483
+ node.start_point in starts
484
+ and (member := _called_member(node, name)) is not None
485
+ ):
486
+ found.append((member.start_point[0] + 1, member.start_point[1]))
487
+ if any(node.start_point[0] <= row <= node.end_point[0] for row, _ in starts):
489
488
  stack.extend(node.children)
490
489
  return sorted(found)
491
490
 
492
491
 
492
+ def _called_member(call: Node, name: str) -> Node | None:
493
+ # The member name a call invokes (`name` in `obj.name(...)`), if it is
494
+ # `name`; None for a bare call or another member.
495
+ func = call.child_by_field_name(cs.FIELD_FUNCTION)
496
+ member = func.children[-1] if func is not None and func.children else None
497
+ if (
498
+ member is not None
499
+ and member.type in _MEMBER_NAME_TYPES
500
+ and member.text is not None
501
+ and member.text.decode(cs.ENCODING_UTF8, errors="replace") == name
502
+ ):
503
+ return member
504
+ return None
505
+
506
+
493
507
  def _jsx_closing_token(
494
508
  source: bytes,
495
509
  language: cs.SupportedLanguage | None,
@@ -614,8 +628,10 @@ def _last_identifier(
614
628
  # (a grammar whose call node has no `function` field, such as Java's
615
629
  # method_invocation) keeps the fallback.
616
630
  return _STALE_CALL
617
- if callee is not None and callee[0] == start:
618
- start, end = callee
631
+ if callee is not None and callee[0] <= start < callee[1]:
632
+ # The callee ends the token search, whether the site starts with it
633
+ # or at its member name (issue #3166): never inside the arguments.
634
+ end = callee[1]
619
635
  text = source[start:end].decode(cs.ENCODING_UTF8, errors="replace")
620
636
  if callee is None:
621
637
  # No grammar: cut at the last opening parenthesis so the arguments
@@ -17,6 +17,7 @@ from .. import constants as cs
17
17
  from .. import cypher_queries as cq
18
18
  from .. import graph_query
19
19
  from ..graph_query import QueryFn
20
+ from ..parsers.utils import call_site_start
20
21
 
21
22
  # Resolutions that bound a site by guesswork: an operation rewrites through
22
23
  # them only when the caller accepts the risk with `allow_heuristic`.
@@ -123,13 +124,14 @@ def call_node_at(
123
124
 
124
125
 
125
126
  def calls_starting_at(root: Node, row: int, col: int) -> list[Node]:
127
+ # A member call's site starts at its name (issue #3166); a graph indexed
128
+ # before that recorded the call node's own start, so both name the call.
126
129
  calls: list[Node] = []
127
130
  stack: list[Node] = [root]
128
131
  while stack:
129
132
  node = stack.pop()
130
- if (
131
- node.start_point == (row, col)
132
- and node.child_by_field_name(cs.FIELD_FUNCTION) is not None
133
+ if node.child_by_field_name(cs.FIELD_FUNCTION) is not None and (
134
+ node.start_point == (row, col) or call_site_start(node) == (row, col)
133
135
  ):
134
136
  calls.append(node)
135
137
  if node.start_point[0] <= row <= node.end_point[0]: