pycode-kg 0.19.0__tar.gz → 0.19.2__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 (66) hide show
  1. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/PKG-INFO +37 -21
  2. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/README.md +32 -15
  3. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/pyproject.toml +5 -11
  4. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/__init__.py +1 -1
  5. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_build.py +11 -5
  6. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_build_full.py +12 -6
  7. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_init.py +1 -1
  8. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_model.py +1 -1
  9. pycode_kg-0.19.2/src/pycode_kg/index.py +11 -0
  10. pycode_kg-0.19.2/src/pycode_kg/module/base.py +33 -0
  11. pycode_kg-0.19.2/src/pycode_kg/module/extractor.py +127 -0
  12. pycode_kg-0.19.2/src/pycode_kg/module/types.py +31 -0
  13. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/pycodekg.py +2 -1
  14. pycode_kg-0.19.2/src/pycode_kg/store.py +7 -0
  15. pycode_kg-0.19.0/src/pycode_kg/index.py +0 -575
  16. pycode_kg-0.19.0/src/pycode_kg/module/base.py +0 -720
  17. pycode_kg-0.19.0/src/pycode_kg/module/extractor.py +0 -276
  18. pycode_kg-0.19.0/src/pycode_kg/module/types.py +0 -532
  19. pycode_kg-0.19.0/src/pycode_kg/store.py +0 -766
  20. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/LICENSE +0 -0
  21. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/.DS_Store +0 -0
  22. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/__main__.py +0 -0
  23. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/analysis/__init__.py +0 -0
  24. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/analysis/bridge.py +0 -0
  25. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/analysis/centrality.py +0 -0
  26. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/analysis/framework_detector.py +0 -0
  27. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/analysis/hybrid_rank.py +0 -0
  28. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/app.py +0 -0
  29. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/architecture.py +0 -0
  30. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/build_pycodekg_lancedb.py +0 -0
  31. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/build_pycodekg_sqlite.py +0 -0
  32. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/__init__.py +0 -0
  33. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_analyze.py +0 -0
  34. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_architecture.py +0 -0
  35. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_bridges.py +0 -0
  36. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_centrality.py +0 -0
  37. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_explain.py +0 -0
  38. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_framework_nodes.py +0 -0
  39. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_hooks.py +0 -0
  40. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_mcp.py +0 -0
  41. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_query.py +0 -0
  42. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_snapshot.py +0 -0
  43. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/cmd_viz.py +0 -0
  44. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/main.py +0 -0
  45. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/cli/options.py +0 -0
  46. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/config.py +0 -0
  47. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/explain.py +0 -0
  48. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/graph.py +0 -0
  49. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/kg.py +0 -0
  50. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/layout3d.py +0 -0
  51. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/mcp_server.py +0 -0
  52. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/module/__init__.py +0 -0
  53. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/pycodekg_query.py +0 -0
  54. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/pycodekg_snippet_packer.py +0 -0
  55. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/pycodekg_thorough_analysis.py +0 -0
  56. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/pycodekg_viz.py +0 -0
  57. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/pycodekg_viz3d.py +0 -0
  58. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/ranking/__init__.py +0 -0
  59. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/ranking/cli_rank.py +0 -0
  60. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/ranking/coderank.py +0 -0
  61. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/snapshots.py +0 -0
  62. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/sql/004_add_centrality_table.sql +0 -0
  63. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/utils.py +0 -0
  64. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/visitor.py +0 -0
  65. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/viz3d.py +0 -0
  66. {pycode_kg-0.19.0 → pycode_kg-0.19.2}/src/pycode_kg/viz3d_timeline.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycode-kg
3
- Version: 0.19.0
3
+ Version: 0.19.2
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
@@ -25,10 +25,9 @@ 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.11.0) ; extra == "dev"
29
- Requires-Dist: doc-kg (>=0.11.0) ; extra == "kgdeps"
30
- Requires-Dist: kgmodule-utils (>=0.2.1)
31
- Requires-Dist: lancedb (>=0.29.0)
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)
32
31
  Requires-Dist: markdown (>=3.6) ; extra == "all"
33
32
  Requires-Dist: markdown (>=3.6) ; extra == "viz3d"
34
33
  Requires-Dist: mcp (>=1.0.0)
@@ -66,7 +65,7 @@ Requires-Dist: streamlit (>=1.35.0) ; extra == "viz"
66
65
  Requires-Dist: torch (>=2.5.1)
67
66
  Requires-Dist: trame-vtk (>=2.0.0) ; extra == "all"
68
67
  Requires-Dist: trame-vtk (>=2.0.0) ; extra == "viz3d"
69
- Requires-Dist: transformers (>=4.57.6)
68
+ Requires-Dist: transformers (>=4.40.0,<4.57)
70
69
  Project-URL: Homepage, https://github.com/Flux-Frontiers/pycode_kg
71
70
  Project-URL: Repository, https://github.com/Flux-Frontiers/pycode_kg
72
71
  Description-Content-Type: text/markdown
@@ -78,7 +77,7 @@ Description-Content-Type: text/markdown
78
77
 
79
78
  [![Python](https://img.shields.io/badge/python-3.12%20%7C%203.13-blue.svg)](https://www.python.org/)
80
79
  [![License: Elastic-2.0](https://img.shields.io/badge/License-Elastic%202.0-blue.svg)](https://www.elastic.co/licensing/elastic-license)
81
- [![Version](https://img.shields.io/badge/version-0.19.0-blue.svg)](https://github.com/Flux-Frontiers/pycode_kg/releases)
80
+ [![Version](https://img.shields.io/badge/version-0.19.1-blue.svg)](https://github.com/Flux-Frontiers/pycode_kg/releases)
82
81
  [![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)
83
82
  [![Poetry](https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json)](https://python-poetry.org/)
84
83
  [![DOI](https://zenodo.org/badge/1202379010.svg)](https://zenodo.org/badge/latestdoi/1202379010)
@@ -117,23 +116,40 @@ PyCodeKG is designed to be useful at both ends — as a standalone command-line
117
116
 
118
117
  ### 1. Standalone — `pycodekg analyze`
119
118
 
120
- This is the bread and butter. One command, one repo, one architectural report:
119
+ This single command is the actual reason I wrote this module. I got tired of my LLM doing grep on my src/ in order to get a good analysis of the codebase. This command runs fifteen analysis passes in seconds and the output drops straight into your favorite coding LLM for targeted improvement suggestions. I used this process to iteratively improve PyCodeKG and several of the analysis passes were added as a result.
120
+
121
+ The analysis/ has files *_analysis_<date>.md - I ran PyCodeKG against **numpy** and **matplotlib** for fun.
121
122
 
122
123
  ```bash
124
+ pycodekg init # downloads embedder
123
125
  pycodekg build --repo . # one-time index
124
- pycodekg analyze . # the report
126
+ pycodekg analyze. # the full report
125
127
  ```
126
128
 
127
- `analyze` walks the graph and produces:
128
-
129
- - **Complexity hotspots** — high fan-in (broadly depended on, breaking-change risk) and high fan-out (orchestrators, refactoring candidates) functions, with risk levels
130
- - **Docstring coverage** broken down by module, class, function, method
131
- - **Circular import cycles** — module loops that cause hard-to-debug failures
132
- - **Orphaned functions** dead-code candidates with line counts (with caveats about entry points and reflection)
133
- - **Module coupling** — the import graph, with the most tightly coupled pairs called out
134
- - **Issues and strengths** high-level callouts suitable for a design review or release note
135
-
136
- It writes a Markdown report for humans and a timestamped JSON snapshot for tooling, CI gates, and trend tracking. Reach for `analyze` before any non-trivial refactor, at every release, and whenever you inherit an unfamiliar codebase. Full reference: [docs/Analyze.md](docs/Analyze.md).
129
+ **Note**
130
+ The first-time run can take some time to warm up. Be patient.
131
+
132
+ The 15-phase pipeline runs in sequence and surfaces several important metrics:
133
+
134
+ | # | Phase | What it surfaces |
135
+ |---|-------|-----------------|
136
+ | 1 | Baseline metrics | Node/edge counts, graph shape |
137
+ | 2 | CodeRank (global PageRank) | Structurally most important symbols |
138
+ | 3 | Fan-in analysis | Heavily depended-on functions (breaking-change risk) |
139
+ | 4 | Fan-out analysis | Orchestrators and complexity hotspots |
140
+ | 5 | Dependency analysis | Orphaned / dead code candidates |
141
+ | 6 | Pattern detection | Anti-patterns and structural red flags |
142
+ | 7 | Module coupling | Tightly coupled pairs, import graph density |
143
+ | 8 | Critical paths | Longest call chains, bottlenecks |
144
+ | 9 | Public API identification | Exposed vs. internal surface |
145
+ | 10 | Docstring coverage | By module, class, function, method |
146
+ | 11 | Inheritance hierarchy | Depth, multiple inheritance, diamond patterns |
147
+ | 12 | Insight synthesis | Issues + strengths callouts |
148
+ | 13 | Snapshot history | Metric trends across releases |
149
+ | 14 | Structural centrality (SIR) | Bridge nodes, graph removal impact |
150
+ | 15 | Concern-based ranking | Nodes grouped by architectural concern |
151
+
152
+ Every finding maps to a file and line number — no hallucinated signatures, no probabilistic guesses. The Markdown output is LLM-ready; the JSON snapshot is CI-gate-ready. Reach for `analyze` before any non-trivial refactor, at every release, and whenever you inherit an unfamiliar codebase. Full reference: [docs/Analyze.md](docs/Analyze.md).
137
153
 
138
154
  ```bash
139
155
  pycodekg analyze --quiet --json ~/.claude/pycodekg_analysis_latest.json
@@ -190,7 +206,7 @@ The graph is built around four node kinds (module, class, function, method) and
190
206
 
191
207
  ---
192
208
 
193
- ## What you can actually do with it
209
+ ## What you can do with it
194
210
 
195
211
  | If you want to… | Reach for | Detail |
196
212
  |---|---|---|
@@ -201,7 +217,7 @@ The graph is built around four node kinds (module, class, function, method) and
201
217
  | **Pull source-grounded context for an LLM** | `pycodekg pack "..." --format md` | [docs/CHEATSHEET.md](docs/CHEATSHEET.md) |
202
218
  | **Run a hybrid semantic + structural query** | `pycodekg query "..."` | [docs/CHEATSHEET.md](docs/CHEATSHEET.md) |
203
219
  | **Browse the graph interactively** | `pycodekg viz` (Streamlit) | [docs/INSTALLATION.md](docs/INSTALLATION.md) |
204
- | **See call graphs in 3-D** | `pycodekg viz3d --layout funnel` | [docs/VIZ3D.md](docs/VIZ3D.md) |
220
+ | **See call graphs in 3-D** *(active development — functional but rough)* | `pycodekg viz3d --layout funnel` | [docs/VIZ3D.md](docs/VIZ3D.md) |
205
221
  | **Wire it into Claude / Copilot / Cline** | `pycodekg mcp` | [docs/MCP.md](docs/MCP.md) |
206
222
 
207
223
  If you only read one doc after this one, read [docs/Analyze.md](docs/Analyze.md) — that's where most of the day-to-day value lives.
@@ -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.0-blue.svg)](https://github.com/Flux-Frontiers/pycode_kg/releases)
8
+ [![Version](https://img.shields.io/badge/version-0.19.1-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)
@@ -44,23 +44,40 @@ PyCodeKG is designed to be useful at both ends — as a standalone command-line
44
44
 
45
45
  ### 1. Standalone — `pycodekg analyze`
46
46
 
47
- This is the bread and butter. One command, one repo, one architectural report:
47
+ This single command is the actual reason I wrote this module. I got tired of my LLM doing grep on my src/ in order to get a good analysis of the codebase. This command runs fifteen analysis passes in seconds and the output drops straight into your favorite coding LLM for targeted improvement suggestions. I used this process to iteratively improve PyCodeKG and several of the analysis passes were added as a result.
48
+
49
+ The analysis/ has files *_analysis_<date>.md - I ran PyCodeKG against **numpy** and **matplotlib** for fun.
48
50
 
49
51
  ```bash
52
+ pycodekg init # downloads embedder
50
53
  pycodekg build --repo . # one-time index
51
- pycodekg analyze . # the report
54
+ pycodekg analyze. # the full report
52
55
  ```
53
56
 
54
- `analyze` walks the graph and produces:
55
-
56
- - **Complexity hotspots** — high fan-in (broadly depended on, breaking-change risk) and high fan-out (orchestrators, refactoring candidates) functions, with risk levels
57
- - **Docstring coverage** broken down by module, class, function, method
58
- - **Circular import cycles** — module loops that cause hard-to-debug failures
59
- - **Orphaned functions** dead-code candidates with line counts (with caveats about entry points and reflection)
60
- - **Module coupling** — the import graph, with the most tightly coupled pairs called out
61
- - **Issues and strengths** high-level callouts suitable for a design review or release note
62
-
63
- It writes a Markdown report for humans and a timestamped JSON snapshot for tooling, CI gates, and trend tracking. Reach for `analyze` before any non-trivial refactor, at every release, and whenever you inherit an unfamiliar codebase. Full reference: [docs/Analyze.md](docs/Analyze.md).
57
+ **Note**
58
+ The first-time run can take some time to warm up. Be patient.
59
+
60
+ The 15-phase pipeline runs in sequence and surfaces several important metrics:
61
+
62
+ | # | Phase | What it surfaces |
63
+ |---|-------|-----------------|
64
+ | 1 | Baseline metrics | Node/edge counts, graph shape |
65
+ | 2 | CodeRank (global PageRank) | Structurally most important symbols |
66
+ | 3 | Fan-in analysis | Heavily depended-on functions (breaking-change risk) |
67
+ | 4 | Fan-out analysis | Orchestrators and complexity hotspots |
68
+ | 5 | Dependency analysis | Orphaned / dead code candidates |
69
+ | 6 | Pattern detection | Anti-patterns and structural red flags |
70
+ | 7 | Module coupling | Tightly coupled pairs, import graph density |
71
+ | 8 | Critical paths | Longest call chains, bottlenecks |
72
+ | 9 | Public API identification | Exposed vs. internal surface |
73
+ | 10 | Docstring coverage | By module, class, function, method |
74
+ | 11 | Inheritance hierarchy | Depth, multiple inheritance, diamond patterns |
75
+ | 12 | Insight synthesis | Issues + strengths callouts |
76
+ | 13 | Snapshot history | Metric trends across releases |
77
+ | 14 | Structural centrality (SIR) | Bridge nodes, graph removal impact |
78
+ | 15 | Concern-based ranking | Nodes grouped by architectural concern |
79
+
80
+ Every finding maps to a file and line number — no hallucinated signatures, no probabilistic guesses. The Markdown output is LLM-ready; the JSON snapshot is CI-gate-ready. Reach for `analyze` before any non-trivial refactor, at every release, and whenever you inherit an unfamiliar codebase. Full reference: [docs/Analyze.md](docs/Analyze.md).
64
81
 
65
82
  ```bash
66
83
  pycodekg analyze --quiet --json ~/.claude/pycodekg_analysis_latest.json
@@ -117,7 +134,7 @@ The graph is built around four node kinds (module, class, function, method) and
117
134
 
118
135
  ---
119
136
 
120
- ## What you can actually do with it
137
+ ## What you can do with it
121
138
 
122
139
  | If you want to… | Reach for | Detail |
123
140
  |---|---|---|
@@ -128,7 +145,7 @@ The graph is built around four node kinds (module, class, function, method) and
128
145
  | **Pull source-grounded context for an LLM** | `pycodekg pack "..." --format md` | [docs/CHEATSHEET.md](docs/CHEATSHEET.md) |
129
146
  | **Run a hybrid semantic + structural query** | `pycodekg query "..."` | [docs/CHEATSHEET.md](docs/CHEATSHEET.md) |
130
147
  | **Browse the graph interactively** | `pycodekg viz` (Streamlit) | [docs/INSTALLATION.md](docs/INSTALLATION.md) |
131
- | **See call graphs in 3-D** | `pycodekg viz3d --layout funnel` | [docs/VIZ3D.md](docs/VIZ3D.md) |
148
+ | **See call graphs in 3-D** *(active development — functional but rough)* | `pycodekg viz3d --layout funnel` | [docs/VIZ3D.md](docs/VIZ3D.md) |
132
149
  | **Wire it into Claude / Copilot / Cline** | `pycodekg mcp` | [docs/MCP.md](docs/MCP.md) |
133
150
 
134
151
  If you only read one doc after this one, read [docs/Analyze.md](docs/Analyze.md) — that's where most of the day-to-day value lives.
@@ -43,11 +43,6 @@
43
43
  requires = ["poetry-core>=2.0.0"]
44
44
  build-backend = "poetry.core.masonry.api"
45
45
 
46
- [[tool.poetry.source]]
47
- name = "testpypi"
48
- url = "https://test.pypi.org/simple/"
49
- priority = "supplemental"
50
-
51
46
  [tool.poetry]
52
47
  packages = [{ include = "pycode_kg", from = "src" }]
53
48
 
@@ -66,7 +61,7 @@ ruff = ">=0.4.0"
66
61
  # ---------------------------------------------------------------------------
67
62
  [project]
68
63
  name = "pycode-kg"
69
- version = "0.19.0"
64
+ version = "0.19.2"
70
65
  description = "A tool to build a searchable knowledge graph from Python repositories"
71
66
  readme = "README.md"
72
67
  license = "Elastic-2.0"
@@ -86,7 +81,6 @@ classifiers = [
86
81
  requires-python = ">=3.12,<3.14"
87
82
  dependencies = [
88
83
  "click>=8.1.0,<9",
89
- "lancedb>=0.29.0",
90
84
  "mcp>=1.0.0",
91
85
  "numpy>=1.24.0",
92
86
  "pandas>=2.0.0",
@@ -94,8 +88,8 @@ dependencies = [
94
88
  "safetensors>=0.5.0",
95
89
  "sentence-transformers>=5.4.1",
96
90
  "torch>=2.5.1",
97
- "transformers>=4.57.6",
98
- "kgmodule-utils>=0.2.1",
91
+ "transformers>=4.40.0,<4.57",
92
+ "kgmodule-utils[semantic]>=0.3.1",
99
93
  ]
100
94
 
101
95
  [project.optional-dependencies]
@@ -108,7 +102,7 @@ dev = [
108
102
  "pytest>=8.0.0",
109
103
  "pytest-cov>=5.0.0",
110
104
  "ruff>=0.4.0",
111
- "doc-kg>=0.11.0",
105
+ "doc-kg>=0.15.2",
112
106
  # agent-kg not yet on PyPI — install manually: pip install git+https://github.com/Flux-Frontiers/agent_kg.git
113
107
  ]
114
108
  viz = [
@@ -127,7 +121,7 @@ viz3d = [
127
121
 
128
122
  # note that dockg is on pypi; agent-kg is not yet — install manually from git
129
123
  kgdeps = [
130
- "doc-kg>=0.11.0",
124
+ "doc-kg>=0.15.2",
131
125
  ]
132
126
  all = [
133
127
  "detect-secrets>=1.5.0",
@@ -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.0"
35
+ __version__ = "0.19.2"
36
36
  __author__ = "Eric G. Suchanek, PhD"
37
37
 
38
38
  # Low-level primitives (locked v0 contract)
@@ -16,12 +16,12 @@ import click
16
16
  from pycode_kg.cli.main import cli
17
17
  from pycode_kg.cli.options import exclude_option, include_option, repo_option
18
18
  from pycode_kg.config import load_exclude_dirs, load_include_dirs
19
- from pycode_kg.graph import CodeGraph
20
19
  from pycode_kg.index import (
21
20
  SemanticIndex,
22
21
  SentenceTransformerEmbedder,
23
22
  suppress_ingestion_logging,
24
23
  )
24
+ from pycode_kg.module.extractor import EdgeSpec, NodeSpec, PyCodeKGExtractor
25
25
  from pycode_kg.pycodekg import DEFAULT_MODEL
26
26
  from pycode_kg.store import GraphStore
27
27
 
@@ -53,19 +53,25 @@ def build_sqlite(
53
53
  include = load_include_dirs(repo_root) | set(include_dir)
54
54
  exclude = load_exclude_dirs(repo_root) | set(exclude_dir)
55
55
 
56
- graph = CodeGraph(
56
+ extractor = PyCodeKGExtractor(
57
57
  repo_root,
58
58
  include=include if include else None,
59
59
  exclude=exclude if exclude else None,
60
60
  )
61
- nodes, edges = graph.extract().result()
61
+ node_specs: list[NodeSpec] = []
62
+ edge_specs: list[EdgeSpec] = []
63
+ for item in extractor.extract():
64
+ if isinstance(item, NodeSpec):
65
+ node_specs.append(item)
66
+ else:
67
+ edge_specs.append(item)
62
68
 
63
69
  store = GraphStore(db_path)
64
- store.write(nodes, edges, wipe=wipe)
70
+ store.write(node_specs, edge_specs, wipe=wipe)
65
71
  resolved = store.resolve_symbols()
66
72
  store.close()
67
73
 
68
- print(f"OK: nodes={len(nodes)} edges={len(edges)} resolved={resolved} db={db_path}")
74
+ print(f"OK: nodes={len(node_specs)} edges={len(edge_specs)} resolved={resolved} db={db_path}")
69
75
 
70
76
 
71
77
  @cli.command("build-lancedb")
@@ -22,12 +22,12 @@ from pycode_kg.cli.options import (
22
22
  repo_option,
23
23
  )
24
24
  from pycode_kg.config import load_exclude_dirs, load_include_dirs
25
- from pycode_kg.graph import CodeGraph
26
25
  from pycode_kg.index import (
27
26
  SemanticIndex,
28
27
  SentenceTransformerEmbedder,
29
28
  suppress_ingestion_logging,
30
29
  )
30
+ from pycode_kg.module.extractor import EdgeSpec, NodeSpec, PyCodeKGExtractor
31
31
  from pycode_kg.store import GraphStore
32
32
 
33
33
 
@@ -126,15 +126,21 @@ def _run_pipeline(
126
126
 
127
127
  # Step 1: Build graph store
128
128
  t1 = time.monotonic()
129
- graph = CodeGraph(
129
+ extractor = PyCodeKGExtractor(
130
130
  repo_root,
131
131
  include=include if include else None,
132
132
  exclude=exclude if exclude else None,
133
133
  )
134
- nodes, edges = graph.extract().result()
134
+ node_specs: list[NodeSpec] = []
135
+ edge_specs: list[EdgeSpec] = []
136
+ for item in extractor.extract():
137
+ if isinstance(item, NodeSpec):
138
+ node_specs.append(item)
139
+ else:
140
+ edge_specs.append(item)
135
141
 
136
142
  store = GraphStore(db_path)
137
- store.write(nodes, edges, wipe=wipe)
143
+ store.write(node_specs, edge_specs, wipe=wipe)
138
144
  resolved = store.resolve_symbols()
139
145
  store.close()
140
146
 
@@ -142,8 +148,8 @@ def _run_pipeline(
142
148
  1,
143
149
  "graph store",
144
150
  [
145
- ("nodes", str(len(nodes))),
146
- ("edges", str(len(edges))),
151
+ ("nodes", str(len(node_specs))),
152
+ ("edges", str(len(edge_specs))),
147
153
  ("resolved", str(resolved)),
148
154
  ],
149
155
  elapsed=time.monotonic() - t1,
@@ -18,6 +18,7 @@ import tomllib
18
18
  from pathlib import Path
19
19
 
20
20
  import click
21
+ from kg_utils.semantic import _local_model_path
21
22
 
22
23
  from pycode_kg.cli.cmd_build_full import _run_pipeline
23
24
  from pycode_kg.cli.cmd_hooks import _PRE_COMMIT_HOOK
@@ -28,7 +29,6 @@ from pycode_kg.cli.options import (
28
29
  model_option,
29
30
  repo_option,
30
31
  )
31
- from pycode_kg.index import _local_model_path
32
32
  from pycode_kg.kg import PyCodeKG
33
33
  from pycode_kg.pycodekg_thorough_analysis import PyCodeKGAnalyzer
34
34
  from pycode_kg.snapshots import SnapshotManager
@@ -9,9 +9,9 @@ CLI command for managing the PyCodeKG embedding model cache.
9
9
  from __future__ import annotations
10
10
 
11
11
  import click
12
+ from kg_utils.semantic import _local_model_path
12
13
 
13
14
  from pycode_kg.cli.main import cli
14
- from pycode_kg.index import _local_model_path
15
15
  from pycode_kg.pycodekg import DEFAULT_MODEL
16
16
 
17
17
 
@@ -0,0 +1,11 @@
1
+ """pycode_kg/index.py — Re-exports semantic index from kg_utils.semantic."""
2
+
3
+ from kg_utils.semantic import ( # noqa: F401
4
+ DEFAULT_MODEL,
5
+ Embedder,
6
+ SeedHit,
7
+ SemanticIndex,
8
+ SentenceTransformerEmbedder,
9
+ resolve_model_path,
10
+ suppress_ingestion_logging,
11
+ )
@@ -0,0 +1,33 @@
1
+ """pycode_kg/module/base.py — KGModule for pycode_kg: thin subclass of kg_utils.pipeline."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from abc import abstractmethod
6
+
7
+ from kg_utils.extractor import KGExtractor
8
+ from kg_utils.pipeline import KGModule as _KGModuleBase
9
+ from kg_utils.store import GraphStore
10
+
11
+
12
+ class KGModule(_KGModuleBase):
13
+ """Abstract base for Python code knowledge graphs.
14
+
15
+ Domain authors implement :meth:`make_extractor`, :meth:`kind`, and
16
+ :meth:`analyze`. Build/query/pack infrastructure comes from
17
+ :class:`kg_utils.pipeline.KGModule`.
18
+ """
19
+
20
+ _default_dir: str = ".pycodekg"
21
+
22
+ def _post_build_hook(self, store: GraphStore) -> None:
23
+ """Run symbol resolution after the graph is written."""
24
+ store.resolve_symbols()
25
+
26
+ @abstractmethod
27
+ def make_extractor(self) -> KGExtractor: ...
28
+
29
+ @abstractmethod
30
+ def kind(self) -> str: ...
31
+
32
+ @abstractmethod
33
+ def analyze(self) -> str: ...
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ module/extractor.py
4
+
5
+ KGExtractor — abstract extraction protocol for any knowledge graph domain.
6
+
7
+ NodeSpec and EdgeSpec are imported from kg_utils.specs and re-exported here.
8
+ PyCodeKGExtractor is the Python-AST-backed implementation.
9
+
10
+ Author: Eric G. Suchanek, PhD
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from collections.abc import Iterator
16
+ from pathlib import Path
17
+ from typing import Any
18
+
19
+ from kg_utils.extractor import KGExtractor # noqa: F401
20
+ from kg_utils.specs import EdgeSpec, NodeSpec # noqa: F401
21
+
22
+ # ---------------------------------------------------------------------------
23
+ # PyCodeKGExtractor — Python-AST-backed extractor
24
+ # ---------------------------------------------------------------------------
25
+
26
+
27
+ class PyCodeKGExtractor(KGExtractor):
28
+ """KGExtractor backed by CodeGraph (Python AST analysis).
29
+
30
+ Wraps :class:`~pycode_kg.graph.CodeGraph` and converts its :class:`Node`
31
+ and :class:`Edge` v0 primitives to :class:`NodeSpec` / :class:`EdgeSpec`
32
+ for the generic :class:`~pycode_kg.module.base.KGModule` build pipeline.
33
+
34
+ This is the extractor used by :class:`~pycode_kg.kg.PyCodeKG` and is the
35
+ reference implementation for source-code KG extractors.
36
+
37
+ :param repo_path: Path to the Python repository root.
38
+ :param include: Set of top-level directory names to include (all if empty).
39
+ :param exclude: Set of directory names to exclude at every depth.
40
+ :param config: Optional config dict (forwarded to super).
41
+ """
42
+
43
+ def __init__(
44
+ self,
45
+ repo_path: Path,
46
+ *,
47
+ include: set[str] | None = None,
48
+ exclude: set[str] | None = None,
49
+ config: dict[str, Any] | None = None,
50
+ ) -> None:
51
+ """Initialise the extractor for a Python repository.
52
+
53
+ :param repo_path: Path to the Python repository root.
54
+ :param include: Top-level directory names to include (empty = all).
55
+ :param exclude: Directory names to exclude at every walk depth.
56
+ :param config: Optional domain-specific configuration dict.
57
+ """
58
+ super().__init__(repo_path, config)
59
+ self._include: set[str] = include or set()
60
+ self._exclude: set[str] = exclude or set()
61
+
62
+ def node_kinds(self) -> list[str]:
63
+ """Return the Python AST node kinds.
64
+
65
+ :return: ``['module', 'class', 'function', 'method', 'symbol']``
66
+ """
67
+ return ["module", "class", "function", "method", "symbol"]
68
+
69
+ def edge_kinds(self) -> list[str]:
70
+ """Return the Python AST edge relation types.
71
+
72
+ :return: List of all relation strings emitted by the Python extractor.
73
+ """
74
+ return [
75
+ "CONTAINS",
76
+ "CALLS",
77
+ "IMPORTS",
78
+ "INHERITS",
79
+ "READS",
80
+ "WRITES",
81
+ "ATTR_ACCESS",
82
+ "DEPENDS_ON",
83
+ "RESOLVES_TO",
84
+ ]
85
+
86
+ def meaningful_node_kinds(self) -> list[str]:
87
+ """Return the node kinds indexed by LanceDB and counted in coverage.
88
+
89
+ Excludes ``'symbol'`` (unresolved import stubs).
90
+
91
+ :return: ``['module', 'class', 'function', 'method']``
92
+ """
93
+ return ["module", "class", "function", "method"]
94
+
95
+ def extract(self) -> Iterator[NodeSpec | EdgeSpec]:
96
+ """Run Python AST extraction and yield NodeSpec / EdgeSpec objects.
97
+
98
+ Delegates to :class:`~pycode_kg.graph.CodeGraph` for the AST walk, then
99
+ converts the v0-locked :class:`~pycode_kg.pycodekg.Node` and
100
+ :class:`~pycode_kg.pycodekg.Edge` primitives to the generic spec types.
101
+
102
+ :return: Iterator of :class:`NodeSpec` and :class:`EdgeSpec` objects.
103
+ """
104
+ from pycode_kg.graph import CodeGraph # pylint: disable=import-outside-toplevel
105
+
106
+ graph = CodeGraph(self.repo_path, include=self._include, exclude=self._exclude)
107
+ nodes, edges = graph.result()
108
+
109
+ for n in nodes:
110
+ yield NodeSpec(
111
+ node_id=n.id,
112
+ kind=n.kind,
113
+ name=n.name,
114
+ qualname=n.qualname or "",
115
+ source_path=n.module_path or "",
116
+ lineno=n.lineno,
117
+ end_lineno=n.end_lineno,
118
+ docstring=n.docstring or "",
119
+ )
120
+
121
+ for e in edges:
122
+ yield EdgeSpec(
123
+ source_id=e.src,
124
+ target_id=e.dst,
125
+ relation=e.rel,
126
+ metadata={"evidence": e.evidence} if e.evidence else {},
127
+ )
@@ -0,0 +1,31 @@
1
+ """pycode_kg/module/types.py — Re-exports from kg_utils for backward compat."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from kg_utils.pipeline import ( # noqa: F401
6
+ compute_span,
7
+ docstring_signal,
8
+ lexical_overlap_score,
9
+ make_module_summary,
10
+ make_snippet,
11
+ normalize_query_text,
12
+ query_tokens,
13
+ read_lines,
14
+ safe_join,
15
+ semantic_score_from_distance,
16
+ spans_overlap,
17
+ )
18
+ from kg_utils.specs import BuildStats, QueryResult, SnippetPack # noqa: F401
19
+
20
+
21
+ @dataclass
22
+ class Snippet:
23
+ """Backward-compat shim. Snippets are now dicts on node["snippet"]."""
24
+
25
+ path: str
26
+ start: int
27
+ end: int
28
+ text: str
29
+
30
+ def to_dict(self) -> dict:
31
+ return {"path": self.path, "start": self.start, "end": self.end, "text": self.text}
@@ -37,7 +37,8 @@ from pathlib import Path
37
37
  # ============================================================================
38
38
  # Configuration
39
39
  # ============================================================================
40
- from pycode_kg.index import DEFAULT_MODEL as DEFAULT_MODEL # noqa: F401 — re-exported
40
+ from kg_utils.semantic import DEFAULT_MODEL as DEFAULT_MODEL # noqa: F401 — re-exported
41
+
41
42
  from pycode_kg.utils import node_id, rel_module_path
42
43
  from pycode_kg.visitor import PyCodeKGVisitor
43
44
 
@@ -0,0 +1,7 @@
1
+ """pycode_kg/store.py — Re-exports GraphStore from kg_utils.store."""
2
+
3
+ from kg_utils.store import ( # noqa: F401
4
+ DEFAULT_RELS,
5
+ GraphStore,
6
+ ProvMeta,
7
+ )