pycode-kg 0.19.2__tar.gz → 0.20.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/PKG-INFO +15 -17
  2. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/README.md +7 -7
  3. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/pyproject.toml +42 -48
  4. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/__init__.py +3 -3
  5. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/analysis/framework_detector.py +1 -1
  6. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/app.py +28 -28
  7. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/architecture.py +1 -1
  8. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/cli/__init__.py +1 -1
  9. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/cli/cmd_analyze.py +4 -4
  10. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/cli/cmd_architecture.py +1 -1
  11. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/cli/cmd_build.py +17 -21
  12. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/cli/cmd_build_full.py +14 -24
  13. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/cli/cmd_explain.py +4 -12
  14. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/cli/cmd_init.py +4 -7
  15. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/cli/cmd_mcp.py +8 -8
  16. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/cli/cmd_model.py +2 -2
  17. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/cli/cmd_query.py +9 -27
  18. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/cli/cmd_snapshot.py +0 -1
  19. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/cli/cmd_viz.py +2 -2
  20. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/cli/options.py +6 -6
  21. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/graph.py +5 -3
  22. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/kg.py +15 -17
  23. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/mcp_server.py +43 -36
  24. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/module/__init__.py +1 -1
  25. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/module/extractor.py +2 -2
  26. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/pycodekg_thorough_analysis.py +20 -20
  27. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/snapshots.py +1 -1
  28. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/viz3d.py +40 -42
  29. pycode_kg-0.19.2/src/pycode_kg/build_pycodekg_lancedb.py +0 -15
  30. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/LICENSE +0 -0
  31. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/.DS_Store +0 -0
  32. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/__main__.py +0 -0
  33. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/analysis/__init__.py +0 -0
  34. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/analysis/bridge.py +0 -0
  35. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/analysis/centrality.py +0 -0
  36. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/analysis/hybrid_rank.py +0 -0
  37. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/build_pycodekg_sqlite.py +0 -0
  38. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/cli/cmd_bridges.py +0 -0
  39. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/cli/cmd_centrality.py +0 -0
  40. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/cli/cmd_framework_nodes.py +0 -0
  41. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/cli/cmd_hooks.py +0 -0
  42. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/cli/main.py +0 -0
  43. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/config.py +0 -0
  44. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/explain.py +0 -0
  45. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/index.py +0 -0
  46. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/layout3d.py +0 -0
  47. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/module/base.py +0 -0
  48. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/module/types.py +0 -0
  49. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/pycodekg.py +0 -0
  50. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/pycodekg_query.py +0 -0
  51. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/pycodekg_snippet_packer.py +0 -0
  52. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/pycodekg_viz.py +0 -0
  53. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/pycodekg_viz3d.py +0 -0
  54. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/ranking/__init__.py +0 -0
  55. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/ranking/cli_rank.py +0 -0
  56. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/ranking/coderank.py +0 -0
  57. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/sql/004_add_centrality_table.sql +0 -0
  58. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/store.py +0 -0
  59. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/utils.py +0 -0
  60. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/visitor.py +0 -0
  61. {pycode_kg-0.19.2 → pycode_kg-0.20.0}/src/pycode_kg/viz3d_timeline.py +0 -0
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycode-kg
3
- Version: 0.19.2
3
+ Version: 0.20.0
4
4
  Summary: A tool to build a searchable knowledge graph from Python repositories
5
5
  License-Expression: Elastic-2.0
6
6
  License-File: LICENSE
7
- Keywords: knowledge-graph,code-analysis,ast,lancedb,sqlite,semantic-search
7
+ Keywords: knowledge-graph,code-analysis,ast,sqlite-vec,sqlite,semantic-search
8
8
  Author: Eric G. Suchanek, PhD
9
- Author-email: suchanek@mac.com
9
+ Author-email: suchanek@flux-frontiers.com
10
10
  Requires-Python: >=3.12,<3.14
11
11
  Classifier: Development Status :: 4 - Beta
12
12
  Classifier: Intended Audience :: Developers
@@ -25,14 +25,12 @@ Requires-Dist: PyQt5 (>=5.15.0) ; extra == "viz3d"
25
25
  Requires-Dist: click (>=8.1.0,<9)
26
26
  Requires-Dist: detect-secrets (>=1.5.0) ; extra == "all"
27
27
  Requires-Dist: detect-secrets (>=1.5.0) ; extra == "dev"
28
- Requires-Dist: doc-kg (>=0.15.2) ; extra == "dev"
29
- Requires-Dist: doc-kg (>=0.15.2) ; extra == "kgdeps"
30
- Requires-Dist: kgmodule-utils[semantic] (>=0.3.1)
28
+ Requires-Dist: doc-kg (>=0.18.1) ; extra == "dev"
29
+ Requires-Dist: doc-kg (>=0.18.1) ; extra == "kgdeps"
30
+ Requires-Dist: kgmodule-utils[semantic,sqlite-vec] (>=0.6.2)
31
31
  Requires-Dist: markdown (>=3.6) ; extra == "all"
32
32
  Requires-Dist: markdown (>=3.6) ; extra == "viz3d"
33
33
  Requires-Dist: mcp (>=1.0.0)
34
- Requires-Dist: mypy (>=1.10.0) ; extra == "all"
35
- Requires-Dist: mypy (>=1.10.0) ; extra == "dev"
36
34
  Requires-Dist: numpy (>=1.24.0)
37
35
  Requires-Dist: pandas (>=2.0.0)
38
36
  Requires-Dist: param (>=2.0.0) ; extra == "all"
@@ -43,8 +41,6 @@ Requires-Dist: plotly (>=5.14.0) ; extra == "all"
43
41
  Requires-Dist: plotly (>=5.14.0) ; extra == "viz"
44
42
  Requires-Dist: pre-commit (>=4.5.1) ; extra == "all"
45
43
  Requires-Dist: pre-commit (>=4.5.1) ; extra == "dev"
46
- Requires-Dist: pylint (>=4.0.5) ; extra == "all"
47
- Requires-Dist: pylint (>=4.0.5) ; extra == "dev"
48
44
  Requires-Dist: pytest (>=8.0.0) ; extra == "all"
49
45
  Requires-Dist: pytest (>=8.0.0) ; extra == "dev"
50
46
  Requires-Dist: pytest-cov (>=5.0.0) ; extra == "all"
@@ -66,6 +62,8 @@ Requires-Dist: torch (>=2.5.1)
66
62
  Requires-Dist: trame-vtk (>=2.0.0) ; extra == "all"
67
63
  Requires-Dist: trame-vtk (>=2.0.0) ; extra == "viz3d"
68
64
  Requires-Dist: transformers (>=4.40.0,<4.57)
65
+ Requires-Dist: ty (>=0.0.41) ; extra == "all"
66
+ Requires-Dist: ty (>=0.0.41) ; extra == "dev"
69
67
  Project-URL: Homepage, https://github.com/Flux-Frontiers/pycode_kg
70
68
  Project-URL: Repository, https://github.com/Flux-Frontiers/pycode_kg
71
69
  Description-Content-Type: text/markdown
@@ -77,7 +75,7 @@ Description-Content-Type: text/markdown
77
75
 
78
76
  [![Python](https://img.shields.io/badge/python-3.12%20%7C%203.13-blue.svg)](https://www.python.org/)
79
77
  [![License: Elastic-2.0](https://img.shields.io/badge/License-Elastic%202.0-blue.svg)](https://www.elastic.co/licensing/elastic-license)
80
- [![Version](https://img.shields.io/badge/version-0.19.1-blue.svg)](https://github.com/Flux-Frontiers/pycode_kg/releases)
78
+ [![Version](https://img.shields.io/badge/version-0.20.0-blue.svg)](https://github.com/Flux-Frontiers/pycode_kg/releases)
81
79
  [![CI](https://github.com/Flux-Frontiers/pycode_kg/actions/workflows/ci.yml/badge.svg)](https://github.com/Flux-Frontiers/pycode_kg/actions/workflows/ci.yml)
82
80
  [![Poetry](https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json)](https://python-poetry.org/)
83
81
  [![DOI](https://zenodo.org/badge/1202379010.svg)](https://zenodo.org/badge/latestdoi/1202379010)
@@ -86,7 +84,7 @@ Description-Content-Type: text/markdown
86
84
 
87
85
  **PyCodeKG turns a Python codebase into a deterministic, queryable knowledge graph — and uses it to produce architectural analyses you can act on, with or without an LLM in the loop.**
88
86
 
89
- It walks the AST of every module, class, function, and method in your repo, extracts the typed relationships that actually hold the code together (`CONTAINS`, `CALLS`, `IMPORTS`, `INHERITS`, `RESOLVES_TO`), and stores the result in SQLite. A LanceDB vector index sits alongside the graph so that *"authentication flow"* and *"verify_jwt"* both find the right place to start exploring. From there you can rank functions by structural importance, trace fan-in across import aliases, detect circular imports and dead code, render the call graph in 3D, snapshot metrics for diffing across releases, or hand the whole thing to Claude over MCP.
87
+ It walks the AST of every module, class, function, and method in your repo, extracts the typed relationships that actually hold the code together (`CONTAINS`, `CALLS`, `IMPORTS`, `INHERITS`, `RESOLVES_TO`), and stores the result in SQLite. A sqlite-vec vector index sits alongside the graph so that *"authentication flow"* and *"verify_jwt"* both find the right place to start exploring. From there you can rank functions by structural importance, trace fan-in across import aliases, detect circular imports and dead code, render the call graph in 3D, snapshot metrics for diffing across releases, or hand the whole thing to Claude over MCP.
90
88
 
91
89
  The original motivation was simple: **produce thorough, defensible analyses of Python codebases that don't depend on inference**. Every result is computed from the AST and the graph — no model is asked to guess. When an LLM is present, it consumes the *same* grounded output as a structured context pack, and the hallucinations that plague "embed-the-repo" tools largely disappear.
92
90
 
@@ -197,7 +195,7 @@ That's the recommended path. Variants (minimal install, MCP-only, contributor se
197
195
 
198
196
  Search is hybrid by design. A query like *"authentication flow"* runs in two phases:
199
197
 
200
- 1. **Vector phase** — the query is embedded with a local sentence-transformer (cached after first download) and LanceDB returns the `k` closest functions, classes, and modules by cosine similarity.
198
+ 1. **Vector phase** — the query is embedded with a local sentence-transformer (cached after first download) and sqlite-vec returns the `k` closest functions, classes, and modules by exact cosine similarity.
201
199
  2. **Graph expansion phase** — each seed hit is expanded `hop` BFS steps along the typed edges (`CONTAINS`, `CALLS`, `IMPORTS`, `INHERITS`, `RESOLVES_TO`) so call chains and module relationships surface alongside the names that matched.
202
200
 
203
201
  **Structure is treated as ground truth; the embeddings are strictly an acceleration layer.** When the graph and the vector index disagree, the graph wins. This is why fan-in lookups are accurate even for same-named symbols across modules — `RESOLVES_TO` edges bridge call sites through their import aliases, and `callers()` does a two-phase reverse traversal that grep simply cannot replicate.
@@ -231,7 +229,7 @@ src/pycode_kg/
231
229
  ├── visitor.py # AST extraction (three-pass: structure, calls, dataflow)
232
230
  ├── graph.py # GraphBuilder: file discovery + dispatch
233
231
  ├── store.py # SQLite persistence + canonical edges
234
- ├── index.py # LanceDB semantic index
232
+ ├── index.py # sqlite-vec semantic index
235
233
  ├── pycodekg.py # Public façade
236
234
  ├── pycodekg_query.py # Hybrid query
237
235
  ├── pycodekg_snippet_packer.py # Source-grounded packs
@@ -273,13 +271,13 @@ If you use PyCodeKG in your research or project, please cite it:
273
271
 
274
272
  [![DOI](https://zenodo.org/badge/1202379010.svg)](https://zenodo.org/badge/latestdoi/1202379010)
275
273
 
276
- > Suchanek, E. G. (2026). *PyCodeKG: A Knowledge Graph for Python Codebases* (Version 0.19.0) [Software]. Flux-Frontiers. https://doi.org/10.5281/zenodo.19834777
274
+ > Suchanek, E. G. (2026). *PyCodeKG: A Knowledge Graph for Python Codebases* (Version 0.20.0) [Software]. Flux-Frontiers. https://doi.org/10.5281/zenodo.19834777
277
275
 
278
276
  ```bibtex
279
277
  @software{suchanek_pycode_kg,
280
278
  author = {Suchanek, Eric G.},
281
279
  title = {{PyCodeKG}: A Knowledge Graph for Python Codebases},
282
- version = {0.19.0},
280
+ version = {0.20.0},
283
281
  year = {2026},
284
282
  publisher = {Flux-Frontiers},
285
283
  url = {https://github.com/Flux-Frontiers/pycode_kg},
@@ -299,7 +297,7 @@ If you use PyCodeKG in your research or project, please cite it:
299
297
 
300
298
  - **Issues** — [GitHub Issues](https://github.com/Flux-Frontiers/pycode_kg/issues)
301
299
  - Sister projects [DocKG](https://github.com/Flux-Frontiers/doc_kg) and [MetaboKG](https://github.com/Flux-Frontiers/metabo_kg)
302
- - LanceDB, sentence-transformers, PyVista, Streamlit, and FastMCP for the foundations
300
+ - sqlite-vec, sentence-transformers, PyVista, Streamlit, and FastMCP for the foundations
303
301
 
304
302
  ---
305
303
 
@@ -5,7 +5,7 @@
5
5
 
6
6
  [![Python](https://img.shields.io/badge/python-3.12%20%7C%203.13-blue.svg)](https://www.python.org/)
7
7
  [![License: Elastic-2.0](https://img.shields.io/badge/License-Elastic%202.0-blue.svg)](https://www.elastic.co/licensing/elastic-license)
8
- [![Version](https://img.shields.io/badge/version-0.19.1-blue.svg)](https://github.com/Flux-Frontiers/pycode_kg/releases)
8
+ [![Version](https://img.shields.io/badge/version-0.20.0-blue.svg)](https://github.com/Flux-Frontiers/pycode_kg/releases)
9
9
  [![CI](https://github.com/Flux-Frontiers/pycode_kg/actions/workflows/ci.yml/badge.svg)](https://github.com/Flux-Frontiers/pycode_kg/actions/workflows/ci.yml)
10
10
  [![Poetry](https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json)](https://python-poetry.org/)
11
11
  [![DOI](https://zenodo.org/badge/1202379010.svg)](https://zenodo.org/badge/latestdoi/1202379010)
@@ -14,7 +14,7 @@
14
14
 
15
15
  **PyCodeKG turns a Python codebase into a deterministic, queryable knowledge graph — and uses it to produce architectural analyses you can act on, with or without an LLM in the loop.**
16
16
 
17
- It walks the AST of every module, class, function, and method in your repo, extracts the typed relationships that actually hold the code together (`CONTAINS`, `CALLS`, `IMPORTS`, `INHERITS`, `RESOLVES_TO`), and stores the result in SQLite. A LanceDB vector index sits alongside the graph so that *"authentication flow"* and *"verify_jwt"* both find the right place to start exploring. From there you can rank functions by structural importance, trace fan-in across import aliases, detect circular imports and dead code, render the call graph in 3D, snapshot metrics for diffing across releases, or hand the whole thing to Claude over MCP.
17
+ It walks the AST of every module, class, function, and method in your repo, extracts the typed relationships that actually hold the code together (`CONTAINS`, `CALLS`, `IMPORTS`, `INHERITS`, `RESOLVES_TO`), and stores the result in SQLite. A sqlite-vec vector index sits alongside the graph so that *"authentication flow"* and *"verify_jwt"* both find the right place to start exploring. From there you can rank functions by structural importance, trace fan-in across import aliases, detect circular imports and dead code, render the call graph in 3D, snapshot metrics for diffing across releases, or hand the whole thing to Claude over MCP.
18
18
 
19
19
  The original motivation was simple: **produce thorough, defensible analyses of Python codebases that don't depend on inference**. Every result is computed from the AST and the graph — no model is asked to guess. When an LLM is present, it consumes the *same* grounded output as a structured context pack, and the hallucinations that plague "embed-the-repo" tools largely disappear.
20
20
 
@@ -125,7 +125,7 @@ That's the recommended path. Variants (minimal install, MCP-only, contributor se
125
125
 
126
126
  Search is hybrid by design. A query like *"authentication flow"* runs in two phases:
127
127
 
128
- 1. **Vector phase** — the query is embedded with a local sentence-transformer (cached after first download) and LanceDB returns the `k` closest functions, classes, and modules by cosine similarity.
128
+ 1. **Vector phase** — the query is embedded with a local sentence-transformer (cached after first download) and sqlite-vec returns the `k` closest functions, classes, and modules by exact cosine similarity.
129
129
  2. **Graph expansion phase** — each seed hit is expanded `hop` BFS steps along the typed edges (`CONTAINS`, `CALLS`, `IMPORTS`, `INHERITS`, `RESOLVES_TO`) so call chains and module relationships surface alongside the names that matched.
130
130
 
131
131
  **Structure is treated as ground truth; the embeddings are strictly an acceleration layer.** When the graph and the vector index disagree, the graph wins. This is why fan-in lookups are accurate even for same-named symbols across modules — `RESOLVES_TO` edges bridge call sites through their import aliases, and `callers()` does a two-phase reverse traversal that grep simply cannot replicate.
@@ -159,7 +159,7 @@ src/pycode_kg/
159
159
  ├── visitor.py # AST extraction (three-pass: structure, calls, dataflow)
160
160
  ├── graph.py # GraphBuilder: file discovery + dispatch
161
161
  ├── store.py # SQLite persistence + canonical edges
162
- ├── index.py # LanceDB semantic index
162
+ ├── index.py # sqlite-vec semantic index
163
163
  ├── pycodekg.py # Public façade
164
164
  ├── pycodekg_query.py # Hybrid query
165
165
  ├── pycodekg_snippet_packer.py # Source-grounded packs
@@ -201,13 +201,13 @@ If you use PyCodeKG in your research or project, please cite it:
201
201
 
202
202
  [![DOI](https://zenodo.org/badge/1202379010.svg)](https://zenodo.org/badge/latestdoi/1202379010)
203
203
 
204
- > Suchanek, E. G. (2026). *PyCodeKG: A Knowledge Graph for Python Codebases* (Version 0.19.0) [Software]. Flux-Frontiers. https://doi.org/10.5281/zenodo.19834777
204
+ > Suchanek, E. G. (2026). *PyCodeKG: A Knowledge Graph for Python Codebases* (Version 0.20.0) [Software]. Flux-Frontiers. https://doi.org/10.5281/zenodo.19834777
205
205
 
206
206
  ```bibtex
207
207
  @software{suchanek_pycode_kg,
208
208
  author = {Suchanek, Eric G.},
209
209
  title = {{PyCodeKG}: A Knowledge Graph for Python Codebases},
210
- version = {0.19.0},
210
+ version = {0.20.0},
211
211
  year = {2026},
212
212
  publisher = {Flux-Frontiers},
213
213
  url = {https://github.com/Flux-Frontiers/pycode_kg},
@@ -227,7 +227,7 @@ If you use PyCodeKG in your research or project, please cite it:
227
227
 
228
228
  - **Issues** — [GitHub Issues](https://github.com/Flux-Frontiers/pycode_kg/issues)
229
229
  - Sister projects [DocKG](https://github.com/Flux-Frontiers/doc_kg) and [MetaboKG](https://github.com/Flux-Frontiers/metabo_kg)
230
- - LanceDB, sentence-transformers, PyVista, Streamlit, and FastMCP for the foundations
230
+ - sqlite-vec, sentence-transformers, PyVista, Streamlit, and FastMCP for the foundations
231
231
 
232
232
  ---
233
233
 
@@ -1,13 +1,13 @@
1
1
  # pyproject.toml — PyCodeKG package configuration (PEP 621)
2
2
  #
3
3
  # Author: Eric G. Suchanek, PhD
4
- # Last Revision: 2026-04-24
4
+ # Last Revision: 2026-07-15
5
5
  #
6
6
  # Build system : Poetry 2.x with PEP 621 [project] table
7
7
  #
8
8
  # Quick install (pip)
9
9
  # -------------------
10
- # pip install -e ".[dev]" core + dev tools (pytest, ruff, mypy, etc.)
10
+ # pip install -e ".[dev]" core + dev tools (pytest, ruff, ty, etc.)
11
11
  # pip install -e ".[viz]" core + Streamlit/Plotly/PyVis visualizer
12
12
  # pip install -e ".[viz3d]" core + PyVista 3D visualizer
13
13
  # pip install -e ".[kgdeps]" core + KG integrations (doc-kg, agent-kg)
@@ -46,29 +46,19 @@ build-backend = "poetry.core.masonry.api"
46
46
  [tool.poetry]
47
47
  packages = [{ include = "pycode_kg", from = "src" }]
48
48
 
49
- [tool.poetry.group.dev.dependencies]
50
- detect-secrets = ">=1.5.0"
51
- mypy = ">=1.10.0"
52
- pdoc = ">=14.0.0"
53
- pre-commit = ">=4.5.1"
54
- pylint = ">=4.0.5"
55
- pytest = ">=8.0.0"
56
- pytest-cov = ">=5.0.0"
57
- ruff = ">=0.4.0"
58
-
59
49
  # ---------------------------------------------------------------------------
60
50
  # PEP 621 project metadata
61
51
  # ---------------------------------------------------------------------------
62
52
  [project]
63
53
  name = "pycode-kg"
64
- version = "0.19.2"
54
+ version = "0.20.0"
65
55
  description = "A tool to build a searchable knowledge graph from Python repositories"
66
56
  readme = "README.md"
67
57
  license = "Elastic-2.0"
68
58
  authors = [
69
- { name = "Eric G. Suchanek, PhD", email = "suchanek@mac.com" }
59
+ { name = "Eric G. Suchanek, PhD", email = "suchanek@flux-frontiers.com" }
70
60
  ]
71
- keywords = ["knowledge-graph", "code-analysis", "ast", "lancedb", "sqlite", "semantic-search"]
61
+ keywords = ["knowledge-graph", "code-analysis", "ast", "sqlite-vec", "sqlite", "semantic-search"]
72
62
  classifiers = [
73
63
  "Development Status :: 4 - Beta",
74
64
  "Intended Audience :: Developers",
@@ -89,20 +79,19 @@ dependencies = [
89
79
  "sentence-transformers>=5.4.1",
90
80
  "torch>=2.5.1",
91
81
  "transformers>=4.40.0,<4.57",
92
- "kgmodule-utils[semantic]>=0.3.1",
82
+ "kgmodule-utils[semantic,sqlite-vec]>=0.6.2",
93
83
  ]
94
84
 
95
85
  [project.optional-dependencies]
96
86
  dev = [
97
87
  "detect-secrets>=1.5.0",
98
- "mypy>=1.10.0",
88
+ "ty>=0.0.41",
99
89
  "pdoc>=14.0.0",
100
90
  "pre-commit>=4.5.1",
101
- "pylint>=4.0.5",
102
91
  "pytest>=8.0.0",
103
92
  "pytest-cov>=5.0.0",
104
93
  "ruff>=0.4.0",
105
- "doc-kg>=0.15.2",
94
+ "doc-kg>=0.18.1",
106
95
  # agent-kg not yet on PyPI — install manually: pip install git+https://github.com/Flux-Frontiers/agent_kg.git
107
96
  ]
108
97
  viz = [
@@ -121,17 +110,15 @@ viz3d = [
121
110
 
122
111
  # note that dockg is on pypi; agent-kg is not yet — install manually from git
123
112
  kgdeps = [
124
- "doc-kg>=0.15.2",
113
+ "doc-kg>=0.18.1",
125
114
  ]
126
115
  all = [
127
116
  "detect-secrets>=1.5.0",
128
117
  "markdown>=3.6",
129
- "mypy>=1.10.0",
130
118
  "param>=2.0.0",
131
119
  "pdoc>=14.0.0",
132
120
  "plotly>=5.14.0",
133
121
  "pre-commit>=4.5.1",
134
- "pylint>=4.0.5",
135
122
  "PyQt5>=5.15.0",
136
123
  "pytest>=8.0.0",
137
124
  "pytest-cov>=5.0.0",
@@ -141,6 +128,7 @@ all = [
141
128
  "ruff>=0.4.0",
142
129
  "streamlit>=1.35.0",
143
130
  "trame-vtk>=2.0.0",
131
+ "ty>=0.0.41",
144
132
  ]
145
133
 
146
134
  [project.urls]
@@ -152,7 +140,7 @@ pycodekg = "pycode_kg.cli.main:cli"
152
140
  pycodekg-analyze = "pycode_kg.cli.cmd_analyze:analyze"
153
141
  pycodekg-architecture = "pycode_kg.cli.cmd_architecture:architecture"
154
142
  pycodekg-build = "pycode_kg.cli.cmd_build_full:build"
155
- pycodekg-build-lancedb = "pycode_kg.cli.cmd_build:build_lancedb"
143
+ pycodekg-build-index = "pycode_kg.cli.cmd_build:build_index"
156
144
  pycodekg-build-sqlite = "pycode_kg.cli.cmd_build:build_sqlite"
157
145
  pycodekg-centrality = "pycode_kg.cli.cmd_centrality:cmd_centrality"
158
146
  pycodekg-download-model = "pycode_kg.cli.cmd_model:download_model"
@@ -172,41 +160,47 @@ pycodekg-viz3d = "pycode_kg.cli.cmd_viz:viz3d"
172
160
  [tool.ruff]
173
161
  line-length = 100
174
162
  target-version = "py312"
163
+ # Vendored Claude Code skills — not project code. The pre-commit hook's
164
+ # exclude can't cover these (it runs with pass_filenames: false), so the
165
+ # exclusion must live here.
166
+ extend-exclude = [".claude"]
175
167
 
176
168
  [tool.ruff.lint]
177
- select = ["E", "F", "W", "I", "UP"]
169
+ # B023, BLE001, PLC0415 replace the retired pylint hook's cell-var-from-loop,
170
+ # broad-exception-caught, and import-outside-toplevel checks.
171
+ select = ["E", "F", "W", "I", "UP", "B023", "BLE001", "PLC0415"]
178
172
  ignore = ["E501"]
179
173
 
180
- [tool.mypy]
181
- python_version = "3.12"
182
- strict = false
183
- ignore_missing_imports = true
184
- mypy_path = "src"
185
- explicit_package_bases = true
174
+ [tool.ruff.lint.per-file-ignores]
175
+ # viz3d is GUI code full of deliberate lazy imports (optional Qt/pyvista deps).
176
+ "src/pycode_kg/viz3d.py" = ["PLC0415"]
177
+ # Test imports are often deliberately lazy — they must run after sys.modules
178
+ # mocks are installed.
179
+ "tests/**" = ["PLC0415"]
180
+
181
+ [tool.ty.environment]
182
+ python-version = "3.12"
183
+ root = ["src"]
184
+
185
+ [tool.ty.rules]
186
+ # Be lenient about third-party stubs (mirrors mypy's ignore_missing_imports).
187
+ unresolved-import = "ignore"
188
+ # viz3d.py carries `# ty: ignore` suppressions for param/PyQt5/pyvista false
189
+ # positives. Those libraries live in the optional viz/viz3d extras, so in the
190
+ # lean CI install the imports are Unknown, the errors don't fire, and the
191
+ # ignores look "unused". Don't fail the build on that — the suppressions are
192
+ # still honored locally where the extras (and the errors) are present.
193
+ unused-ignore-comment = "ignore"
186
194
 
187
195
  [tool.pytest.ini_options]
188
- testpaths = ["tests"]
189
- addopts = "-v --tb=short"
196
+ testpaths = ["tests"]
197
+ addopts = "-v --tb=short"
198
+ pythonpath = ["src"]
190
199
  markers = [
191
200
  "slow: marks tests as slow (deselect with '-m not slow')",
192
- "integration: marks tests that exercise real external dependencies (model, LanceDB)",
193
- ]
194
-
195
- [tool.pylint.messages_control]
196
- disable = ["all"]
197
- enable = [
198
- "broad-exception-caught", # W0718
199
- "cell-var-from-loop", # W0640
200
- "cyclic-import", # R0401
201
- "import-outside-toplevel", # C0415
202
- "undefined-variable", # E0602
201
+ "integration: marks tests that exercise real external dependencies (model, sqlite-vec)",
203
202
  ]
204
203
 
205
- [tool.pylint.imports]
206
- # The kg → pycodekg_thorough_analysis cycle is broken at runtime by a lazy
207
- # import inside PyCodeKG.analyze(). Pylint's static analysis can't see that.
208
- ignored-modules = ["pycode_kg.pycodekg_thorough_analysis"]
209
-
210
204
  # ---------------------------------------------------------------------------
211
205
  # PyCodeKG / DocKG index configuration
212
206
  # ---------------------------------------------------------------------------
@@ -1,7 +1,7 @@
1
1
  """
2
2
  pycode_kg: A tool to build a searchable knowledge graph from Python repositories.
3
3
 
4
- Pure AST extraction → SQLite (authoritative) → LanceDB (semantic index).
4
+ Pure AST extraction → SQLite (authoritative) → sqlite-vec (semantic index).
5
5
 
6
6
  Public API
7
7
  ----------
@@ -9,7 +9,7 @@ Primary entry point::
9
9
 
10
10
  from pycode_kg import PyCodeKG
11
11
 
12
- kg = PyCodeKG(repo_root, db_path, lancedb_dir)
12
+ kg = PyCodeKG(repo_root, db_path)
13
13
  stats = kg.build(wipe=True)
14
14
  result = kg.query("database connection setup")
15
15
  pack = kg.pack("configuration loading")
@@ -32,7 +32,7 @@ KGModule SDK (build new domain KGs)::
32
32
  from pycode_kg import KGModule, KGExtractor, PyCodeKGExtractor, NodeSpec, EdgeSpec
33
33
  """
34
34
 
35
- __version__ = "0.19.2"
35
+ __version__ = "0.20.0"
36
36
  __author__ = "Eric G. Suchanek, PhD"
37
37
 
38
38
  # Low-level primitives (locked v0 contract)
@@ -62,7 +62,7 @@ def detect_framework_nodes(
62
62
  # against the module_connectivity metric. Both signals end up keyed by
63
63
  # bare module path (e.g. "src/pycode_kg/store.py"), which we map to "mod:..."
64
64
  # IDs for output.
65
- from pycode_kg.analysis.centrality import ( # pylint: disable=import-outside-toplevel
65
+ from pycode_kg.analysis.centrality import ( # noqa: PLC0415
66
66
  StructuralImportanceRanker,
67
67
  aggregate_module_scores,
68
68
  )
@@ -57,7 +57,7 @@ _REL_COLOR: dict[str, str] = {
57
57
  import os as _os # noqa: E402
58
58
 
59
59
  _DEFAULT_DB = _os.environ.get("PYCODEKG_DB", ".pycodekg/graph.sqlite")
60
- _DEFAULT_LANCEDB = _os.environ.get("PYCODEKG_LANCEDB", ".pycodekg/lancedb")
60
+ _DEFAULT_VECTORS = _os.environ.get("PYCODEKG_VECTORS", ".pycodekg/vectors.sqlite")
61
61
 
62
62
  # ---------------------------------------------------------------------------
63
63
  # Page config (must be first Streamlit call)
@@ -147,7 +147,7 @@ def _get_store() -> GraphStore | None:
147
147
 
148
148
 
149
149
  @st.cache_resource(show_spinner="Loading PyCodeKG (embedder may take a moment)…")
150
- def _load_kg(repo_root: str, db_path: str, lancedb_dir: str, model: str):
150
+ def _load_kg(repo_root: str, db_path: str, vectors_path: str, model: str):
151
151
  """
152
152
  Load and cache a ``PyCodeKG`` instance for the given configuration.
153
153
 
@@ -157,16 +157,16 @@ def _load_kg(repo_root: str, db_path: str, lancedb_dir: str, model: str):
157
157
 
158
158
  :param repo_root: Root directory of the Python repository to analyse.
159
159
  :param db_path: Path to the SQLite graph database.
160
- :param lancedb_dir: Directory for the LanceDB vector index.
160
+ :param vectors_path: Path to the sqlite-vec vector store.
161
161
  :param model: Name of the sentence-transformer embedding model to use.
162
162
  :return: An initialised ``PyCodeKG`` instance.
163
163
  """
164
- from pycode_kg import PyCodeKG # pylint: disable=import-outside-toplevel
164
+ from pycode_kg import PyCodeKG # noqa: PLC0415
165
165
 
166
166
  return PyCodeKG(
167
167
  repo_root=repo_root,
168
168
  db_path=db_path,
169
- lancedb_dir=lancedb_dir,
169
+ vectors_path=vectors_path,
170
170
  model=model,
171
171
  )
172
172
 
@@ -592,7 +592,7 @@ def _render_node_detail(node: dict, store: GraphStore | None = None) -> None:
592
592
  (node_id, node_id),
593
593
  ).fetchall()
594
594
  if rows:
595
- import pandas as pd # pylint: disable=import-outside-toplevel # pylint: disable=import-outside-toplevel
595
+ import pandas as pd # noqa: PLC0415
596
596
 
597
597
  edf = pd.DataFrame([{"src": r[0], "rel": r[1], "dst": r[2]} for r in rows])
598
598
  st.dataframe(edf, use_container_width=True, hide_index=True)
@@ -694,12 +694,12 @@ def _render_sidebar() -> dict:
694
694
  """
695
695
  Render the sidebar controls and return a configuration dictionary.
696
696
 
697
- Exposes controls for the SQLite path, repo root, LanceDB directory,
697
+ Exposes controls for the SQLite path, repo root, sqlite-vec store path,
698
698
  embedding model, query parameters (k, hops, relations, include_symbols),
699
699
  graph display options (max nodes, physics, height), and build buttons
700
700
  for the graph and semantic index.
701
701
 
702
- :return: A dict with keys ``db_path``, ``repo_root``, ``lancedb_dir``,
702
+ :return: A dict with keys ``db_path``, ``repo_root``, ``vectors_path``,
703
703
  ``model``, ``k``, ``hop``, ``rels``, ``include_symbols``,
704
704
  ``max_graph_nodes``, ``physics_on``, ``graph_height``, and ``store``.
705
705
  """
@@ -738,10 +738,10 @@ def _render_sidebar() -> dict:
738
738
  value=str(Path.cwd()),
739
739
  help="Root directory of the Python repository to analyse",
740
740
  )
741
- lancedb_dir = st.sidebar.text_input(
742
- "LanceDB dir",
743
- value=_DEFAULT_LANCEDB,
744
- help="Directory for the LanceDB vector index",
741
+ vectors_path = st.sidebar.text_input(
742
+ "Vector store path",
743
+ value=_DEFAULT_VECTORS,
744
+ help="Path to the sqlite-vec vector store",
745
745
  )
746
746
  model = st.sidebar.selectbox(
747
747
  "Embedding model",
@@ -789,12 +789,12 @@ def _render_sidebar() -> dict:
789
789
  )
790
790
  build_index_btn = build_col2.button(
791
791
  "🧠 Build Index",
792
- help="Embed nodes → LanceDB (requires graph to exist)",
792
+ help="Embed nodes → sqlite-vec (requires graph to exist)",
793
793
  use_container_width=True,
794
794
  )
795
795
  build_all_btn = st.sidebar.button(
796
796
  "⚡ Build All (graph + index)",
797
- help="Full pipeline: AST → SQLite → LanceDB",
797
+ help="Full pipeline: AST → SQLite → sqlite-vec",
798
798
  use_container_width=True,
799
799
  type="primary",
800
800
  )
@@ -803,14 +803,14 @@ def _render_sidebar() -> dict:
803
803
  with st.sidebar:
804
804
  with st.spinner("Building graph (AST → SQLite)…"):
805
805
  try:
806
- from pycode_kg import ( # pylint: disable=import-outside-toplevel
806
+ from pycode_kg import ( # noqa: PLC0415
807
807
  PyCodeKG,
808
808
  )
809
809
 
810
810
  kg = PyCodeKG(
811
811
  repo_root=repo_root,
812
812
  db_path=db_path,
813
- lancedb_dir=lancedb_dir,
813
+ vectors_path=vectors_path,
814
814
  model=model,
815
815
  )
816
816
  if build_all_btn:
@@ -835,16 +835,16 @@ def _render_sidebar() -> dict:
835
835
 
836
836
  if build_index_btn and not build_all_btn:
837
837
  with st.sidebar:
838
- with st.spinner("Building semantic index (SQLite → LanceDB)…"):
838
+ with st.spinner("Building semantic index (SQLite → sqlite-vec)…"):
839
839
  try:
840
- from pycode_kg import ( # pylint: disable=import-outside-toplevel
840
+ from pycode_kg import ( # noqa: PLC0415
841
841
  PyCodeKG,
842
842
  )
843
843
 
844
844
  kg = PyCodeKG(
845
845
  repo_root=repo_root,
846
846
  db_path=db_path,
847
- lancedb_dir=lancedb_dir,
847
+ vectors_path=vectors_path,
848
848
  model=model,
849
849
  )
850
850
  stats = kg.build_index(wipe=True)
@@ -856,7 +856,7 @@ def _render_sidebar() -> dict:
856
856
  return {
857
857
  "db_path": db_path,
858
858
  "repo_root": repo_root,
859
- "lancedb_dir": lancedb_dir,
859
+ "vectors_path": vectors_path,
860
860
  "model": model,
861
861
  "k": k,
862
862
  "hop": hop,
@@ -945,7 +945,7 @@ def _tab_graph(cfg: dict) -> None:
945
945
  st.iframe(html, height=int(cfg["graph_height"].replace("px", "")))
946
946
 
947
947
  with st.expander("📋 Node table"):
948
- import pandas as pd # pylint: disable=import-outside-toplevel
948
+ import pandas as pd # noqa: PLC0415
949
949
 
950
950
  df = pd.DataFrame(
951
951
  [
@@ -984,7 +984,7 @@ def _tab_query(cfg: dict) -> None:
984
984
 
985
985
  :param cfg: Configuration dictionary returned by ``_render_sidebar``,
986
986
  providing keys such as ``store``, ``repo_root``, ``db_path``,
987
- ``lancedb_dir``, ``model``, ``k``, ``hop``, ``rels``,
987
+ ``vectors_path``, ``model``, ``k``, ``hop``, ``rels``,
988
988
  ``include_symbols``, ``graph_height``, and ``physics_on``.
989
989
  """
990
990
  st.header("🔍 Hybrid Query")
@@ -1007,7 +1007,7 @@ def _tab_query(cfg: dict) -> None:
1007
1007
  kg = _load_kg(
1008
1008
  cfg["repo_root"],
1009
1009
  cfg["db_path"],
1010
- cfg["lancedb_dir"],
1010
+ cfg["vectors_path"],
1011
1011
  cfg["model"],
1012
1012
  )
1013
1013
  result = kg.query(
@@ -1052,7 +1052,7 @@ def _tab_query(cfg: dict) -> None:
1052
1052
  st.info("No nodes to display.")
1053
1053
 
1054
1054
  with tab_table:
1055
- import pandas as pd # pylint: disable=import-outside-toplevel
1055
+ import pandas as pd # noqa: PLC0415
1056
1056
 
1057
1057
  df = pd.DataFrame(
1058
1058
  [
@@ -1075,7 +1075,7 @@ def _tab_query(cfg: dict) -> None:
1075
1075
 
1076
1076
  with tab_edges:
1077
1077
  if result.edges:
1078
- import pandas as pd # pylint: disable=import-outside-toplevel
1078
+ import pandas as pd # noqa: PLC0415
1079
1079
 
1080
1080
  edf = pd.DataFrame(
1081
1081
  [
@@ -1119,7 +1119,7 @@ def _tab_snippets(cfg: dict) -> None:
1119
1119
 
1120
1120
  :param cfg: Configuration dictionary returned by ``_render_sidebar``,
1121
1121
  providing keys such as ``store``, ``repo_root``, ``db_path``,
1122
- ``lancedb_dir``, ``model``, ``k``, ``hop``, ``rels``,
1122
+ ``vectors_path``, ``model``, ``k``, ``hop``, ``rels``,
1123
1123
  ``include_symbols``, and ``physics_on``.
1124
1124
  """
1125
1125
  st.header("📦 Snippet Pack")
@@ -1152,7 +1152,7 @@ def _tab_snippets(cfg: dict) -> None:
1152
1152
  kg = _load_kg(
1153
1153
  cfg["repo_root"],
1154
1154
  cfg["db_path"],
1155
- cfg["lancedb_dir"],
1155
+ cfg["vectors_path"],
1156
1156
  cfg["model"],
1157
1157
  )
1158
1158
  pack = kg.pack(
@@ -1249,7 +1249,7 @@ def _tab_snippets(cfg: dict) -> None:
1249
1249
  # Edges table
1250
1250
  if pack.edges:
1251
1251
  with st.expander(f"🔗 Edges ({len(pack.edges)})"):
1252
- import pandas as pd # pylint: disable=import-outside-toplevel
1252
+ import pandas as pd # noqa: PLC0415
1253
1253
 
1254
1254
  edf = pd.DataFrame(
1255
1255
  [
@@ -460,7 +460,7 @@ class ArchitectureAnalyzer:
460
460
  "name": "Graph Query Pipeline",
461
461
  "description": "Semantic search → graph expansion → snippet packing",
462
462
  "steps": [
463
- "Semantic search finds seed nodes via LanceDB",
463
+ "Semantic search finds seed nodes via sqlite-vec",
464
464
  "Graph expansion traverses CALLS, CONTAINS, IMPORTS edges",
465
465
  "Snippet pack materializes source code with context",
466
466
  ],
@@ -12,7 +12,7 @@ The root Click group is importable from either location::
12
12
  from pycode_kg.cli import ( # noqa: F401 — registers snapshot (save, list, show, diff)
13
13
  cmd_analyze, # noqa: F401 — registers analyze
14
14
  cmd_architecture, # noqa: F401 — registers architecture
15
- cmd_build, # noqa: F401 — registers build-sqlite, build-lancedb
15
+ cmd_build, # noqa: F401 — registers build-sqlite, build-index
16
16
  cmd_build_full, # noqa: F401 — registers build (full pipeline), update (incremental upsert)
17
17
  cmd_centrality, # noqa: F401 — registers centrality
18
18
  cmd_explain, # noqa: F401 — registers explain
@@ -27,10 +27,10 @@ from pycode_kg.pycodekg_thorough_analysis import main as run_analysis
27
27
  help="SQLite knowledge graph path (default: <repo>/.pycodekg/graph.sqlite).",
28
28
  )
29
29
  @click.option(
30
- "--lancedb",
30
+ "--vectors",
31
31
  default=None,
32
32
  type=click.Path(),
33
- help="LanceDB vector index directory (default: <repo>/.pycodekg/lancedb).",
33
+ help="sqlite-vec vector store path (default: <repo>/.pycodekg/vectors.sqlite).",
34
34
  )
35
35
  @click.option(
36
36
  "--output",
@@ -63,7 +63,7 @@ from pycode_kg.pycodekg_thorough_analysis import main as run_analysis
63
63
  def analyze(
64
64
  repo_root: str,
65
65
  db: str | None,
66
- lancedb: str | None,
66
+ vectors: str | None,
67
67
  output: str | None,
68
68
  json_path: str | None,
69
69
  quiet: bool,
@@ -86,7 +86,7 @@ def analyze(
86
86
  run_analysis(
87
87
  repo_root=repo_root,
88
88
  db_path=db,
89
- lancedb_path=lancedb,
89
+ vectors_path=vectors,
90
90
  report_path=output,
91
91
  json_path=json_path,
92
92
  quiet=quiet,