pycode-kg 0.23.0__tar.gz → 0.24.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 (63) hide show
  1. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/PKG-INFO +9 -9
  2. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/README.md +4 -4
  3. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/pyproject.toml +31 -8
  4. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/__init__.py +1 -1
  5. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/analysis/bridge.py +0 -1
  6. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/analysis/centrality.py +0 -1
  7. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/analysis/framework_detector.py +0 -1
  8. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/cli/cmd_hooks.py +52 -11
  9. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/cli/cmd_init.py +4 -0
  10. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/cli/cmd_quilt.py +0 -1
  11. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/cli/cmd_snapshot.py +4 -0
  12. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/pycodekg.py +0 -1
  13. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/pycodekg_thorough_analysis.py +266 -73
  14. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/report.py +39 -2
  15. pycode_kg-0.24.0/src/pycode_kg/resolution.py +211 -0
  16. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/scene3d.py +5 -51
  17. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/snapshots.py +16 -1
  18. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/viz3d.py +45 -47
  19. pycode_kg-0.23.0/src/pycode_kg/resolution.py +0 -95
  20. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/LICENSE +0 -0
  21. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/.DS_Store +0 -0
  22. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/__main__.py +0 -0
  23. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/analysis/__init__.py +0 -0
  24. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/app.py +0 -0
  25. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/architecture.py +0 -0
  26. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/build_pycodekg_sqlite.py +0 -0
  27. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/cli/__init__.py +0 -0
  28. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/cli/cmd_analyze.py +0 -0
  29. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/cli/cmd_architecture.py +0 -0
  30. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/cli/cmd_bridges.py +0 -0
  31. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/cli/cmd_build.py +0 -0
  32. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/cli/cmd_build_full.py +0 -0
  33. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/cli/cmd_centrality.py +0 -0
  34. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/cli/cmd_explain.py +0 -0
  35. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/cli/cmd_framework_nodes.py +0 -0
  36. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/cli/cmd_mcp.py +0 -0
  37. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/cli/cmd_model.py +0 -0
  38. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/cli/cmd_query.py +0 -0
  39. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/cli/cmd_viz.py +0 -0
  40. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/cli/main.py +0 -0
  41. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/cli/options.py +0 -0
  42. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/config.py +0 -0
  43. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/explain.py +0 -0
  44. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/graph.py +0 -0
  45. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/graph_html.py +0 -0
  46. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/index.py +0 -0
  47. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/kg.py +0 -0
  48. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/layout3d.py +0 -0
  49. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/mcp_server.py +0 -0
  50. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/module/__init__.py +0 -0
  51. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/module/base.py +0 -0
  52. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/module/extractor.py +0 -0
  53. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/module/types.py +0 -0
  54. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/ranking/__init__.py +0 -0
  55. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/ranking/cli_rank.py +0 -0
  56. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/ranking/coderank.py +0 -0
  57. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/render.py +0 -0
  58. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/sql/004_add_centrality_table.sql +0 -0
  59. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/store.py +0 -0
  60. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/theme.py +0 -0
  61. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/utils.py +0 -0
  62. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/src/pycode_kg/visitor.py +0 -0
  63. {pycode_kg-0.23.0 → pycode_kg-0.24.0}/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.23.0
3
+ Version: 0.24.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
@@ -21,8 +21,8 @@ Provides-Extra: viz3d
21
21
  Requires-Dist: PyQt5 (>=5.15.0) ; extra == "all"
22
22
  Requires-Dist: PyQt5 (>=5.15.0) ; extra == "viz3d"
23
23
  Requires-Dist: click (>=8.1.0,<9)
24
- Requires-Dist: kgmodule-utils[semantic,viz3d] (>=0.12.1)
25
- Requires-Dist: kgmodule-utils[viz3d-render] (>=0.12.1) ; extra == "viz3d"
24
+ Requires-Dist: kgmodule-utils[semantic,viz3d] (>=0.18.0)
25
+ Requires-Dist: kgmodule-utils[viz3d-qt,viz3d-render] (>=0.18.0) ; extra == "viz3d"
26
26
  Requires-Dist: markdown (>=3.6) ; extra == "all"
27
27
  Requires-Dist: markdown (>=3.6) ; extra == "viz3d"
28
28
  Requires-Dist: mcp (>=1.0.0,<2)
@@ -38,8 +38,8 @@ Requires-Dist: pyvis (>=0.3.2) ; extra == "viz"
38
38
  Requires-Dist: pyvista (>=0.44.0) ; extra == "all"
39
39
  Requires-Dist: pyvistaqt (>=0.11.0) ; extra == "all"
40
40
  Requires-Dist: pyvistaqt (>=0.11.0) ; extra == "viz3d"
41
- Requires-Dist: quiltwright (>=0.4.0) ; extra == "all"
42
- Requires-Dist: quiltwright (>=0.4.0) ; extra == "viz3d"
41
+ Requires-Dist: quiltwright (>=0.8.0) ; extra == "all"
42
+ Requires-Dist: quiltwright (>=0.8.0) ; extra == "viz3d"
43
43
  Requires-Dist: rich (>=14.3.3,<15)
44
44
  Requires-Dist: sentence-transformers (>=5.4.1)
45
45
  Requires-Dist: sqlite-vec (==0.1.9)
@@ -60,7 +60,7 @@ Description-Content-Type: text/markdown
60
60
 
61
61
  [![Python](https://img.shields.io/badge/python-3.12%20%7C%203.13-blue.svg)](https://www.python.org/)
62
62
  [![License: Elastic-2.0](https://img.shields.io/badge/License-Elastic%202.0-blue.svg)](https://www.elastic.co/licensing/elastic-license)
63
- [![Version](https://img.shields.io/badge/version-0.23.0-blue.svg)](https://github.com/Flux-Frontiers/pycode_kg/releases)
63
+ [![Version](https://img.shields.io/badge/version-0.24.0-blue.svg)](https://github.com/Flux-Frontiers/pycode_kg/releases)
64
64
  [![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)
65
65
  [![Poetry](https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json)](https://python-poetry.org/)
66
66
  [![DOI](https://zenodo.org/badge/1202379010.svg)](https://zenodo.org/badge/latestdoi/1202379010)
@@ -255,7 +255,7 @@ src/pycode_kg/
255
255
  └── viz3d_timeline.py # Metric history timeline
256
256
  ```
257
257
 
258
- The MCP server, the CLI, and the Streamlit app are thin wrappers over the same store + index + ranking core — there is exactly one code path for each capability. The latest architectural deep-dive is in [docs/analysis_v0.23.0.md](docs/analysis_v0.23.0.md), produced (of course) by `pycodekg analyze` against this very repo.
258
+ The MCP server, the CLI, and the Streamlit app are thin wrappers over the same store + index + ranking core — there is exactly one code path for each capability. The latest architectural deep-dive is in [docs/analysis_v0.24.0.md](docs/analysis_v0.24.0.md), produced (of course) by `pycodekg analyze` against this very repo.
259
259
 
260
260
  ---
261
261
 
@@ -282,13 +282,13 @@ If you use PyCodeKG in your research or project, please cite it:
282
282
 
283
283
  [![DOI](https://zenodo.org/badge/1202379010.svg)](https://zenodo.org/badge/latestdoi/1202379010)
284
284
 
285
- > Suchanek, E. G. (2026). *PyCodeKG: A Knowledge Graph for Python Codebases* (Version 0.23.0) [Software]. Flux-Frontiers. https://doi.org/10.5281/zenodo.19737993
285
+ > Suchanek, E. G. (2026). *PyCodeKG: A Knowledge Graph for Python Codebases* (Version 0.24.0) [Software]. Flux-Frontiers. https://doi.org/10.5281/zenodo.19737993
286
286
 
287
287
  ```bibtex
288
288
  @software{suchanek_pycode_kg,
289
289
  author = {Suchanek, Eric G.},
290
290
  title = {{PyCodeKG}: A Knowledge Graph for Python Codebases},
291
- version = {0.23.0},
291
+ version = {0.24.0},
292
292
  year = {2026},
293
293
  publisher = {Flux-Frontiers},
294
294
  url = {https://github.com/Flux-Frontiers/pycode_kg},
@@ -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.23.0-blue.svg)](https://github.com/Flux-Frontiers/pycode_kg/releases)
8
+ [![Version](https://img.shields.io/badge/version-0.24.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)
@@ -200,7 +200,7 @@ src/pycode_kg/
200
200
  └── viz3d_timeline.py # Metric history timeline
201
201
  ```
202
202
 
203
- The MCP server, the CLI, and the Streamlit app are thin wrappers over the same store + index + ranking core — there is exactly one code path for each capability. The latest architectural deep-dive is in [docs/analysis_v0.23.0.md](docs/analysis_v0.23.0.md), produced (of course) by `pycodekg analyze` against this very repo.
203
+ The MCP server, the CLI, and the Streamlit app are thin wrappers over the same store + index + ranking core — there is exactly one code path for each capability. The latest architectural deep-dive is in [docs/analysis_v0.24.0.md](docs/analysis_v0.24.0.md), produced (of course) by `pycodekg analyze` against this very repo.
204
204
 
205
205
  ---
206
206
 
@@ -227,13 +227,13 @@ If you use PyCodeKG in your research or project, please cite it:
227
227
 
228
228
  [![DOI](https://zenodo.org/badge/1202379010.svg)](https://zenodo.org/badge/latestdoi/1202379010)
229
229
 
230
- > Suchanek, E. G. (2026). *PyCodeKG: A Knowledge Graph for Python Codebases* (Version 0.23.0) [Software]. Flux-Frontiers. https://doi.org/10.5281/zenodo.19737993
230
+ > Suchanek, E. G. (2026). *PyCodeKG: A Knowledge Graph for Python Codebases* (Version 0.24.0) [Software]. Flux-Frontiers. https://doi.org/10.5281/zenodo.19737993
231
231
 
232
232
  ```bibtex
233
233
  @software{suchanek_pycode_kg,
234
234
  author = {Suchanek, Eric G.},
235
235
  title = {{PyCodeKG}: A Knowledge Graph for Python Codebases},
236
- version = {0.23.0},
236
+ version = {0.24.0},
237
237
  year = {2026},
238
238
  publisher = {Flux-Frontiers},
239
239
  url = {https://github.com/Flux-Frontiers/pycode_kg},
@@ -1,7 +1,6 @@
1
1
  # pyproject.toml — PyCodeKG package configuration (PEP 621)
2
2
  #
3
3
  # Author: Eric G. Suchanek, PhD
4
- # Last Revision: 2026-08-15
5
4
  #
6
5
  # Build system : Poetry 2.x with PEP 621 [project] table
7
6
  #
@@ -71,7 +70,7 @@ torch = [
71
70
  # ---------------------------------------------------------------------------
72
71
  [project]
73
72
  name = "pycode-kg"
74
- version = "0.23.0"
73
+ version = "0.24.0"
75
74
  description = "A tool to build a searchable knowledge graph from Python repositories"
76
75
  readme = "README.md"
77
76
  license = "Elastic-2.0"
@@ -137,7 +136,21 @@ dependencies = [
137
136
  # Its only dependency is numpy, which `[semantic]` already pulls, so it adds
138
137
  # nothing to the install — it just declares what layout3d actually imports.
139
138
  # Unlike `[viz]`, there is no pyvis to leak into a bare install.
140
- "kgmodule-utils[semantic,viz3d]>=0.12.1",
139
+ # Floor is 0.13.x rather than 0.12.1 because that is where SnapshotManager
140
+ # stops writing absolute paths into snapshot JSON. This package ships
141
+ # `pycodekg snapshot save`, and those snapshots get committed to the
142
+ # user's repo — against an older kgmodule-utils the command writes their
143
+ # home directory and username into version control.
144
+ #
145
+ # 0.13.2 specifically, never 0.13.1: that release carried the path fix but
146
+ # also made `repo_root` a read-only property, which breaks every subclass
147
+ # that assigns it. 0.13.2 keeps the fix and restores the plain attribute.
148
+ #
149
+ # 0.15.0 because the `viz3d` extra below needs kg_utils.viz3d.qt, and this
150
+ # repo's own rule is one floor per package — a lower floor here would leave
151
+ # a reader working out which constraint actually binds. Nothing in the core
152
+ # install needs 0.15.0 functionally; it is a superset of 0.14.0.
153
+ "kgmodule-utils[semantic,viz3d]>=0.18.0",
141
154
  ]
142
155
 
143
156
  [project.optional-dependencies]
@@ -154,12 +167,22 @@ viz3d = [
154
167
  # and smooth_paths live there. The main dependency above stays on plain
155
168
  # [viz3d] (numpy only) so the nine repos that take pycode-kg do not acquire
156
169
  # VTK for a layout import; rendering stays opt-in, here.
157
- "kgmodule-utils[viz3d-render]>=0.12.1",
170
+ # Same floor as the core declaration above — one package, one floor, so a
171
+ # reader does not have to work out which constraint actually binds.
172
+ # [viz3d-qt] (new in 0.15.0) adds kg_utils.viz3d.qt — the Qt machinery a
173
+ # viewer needs to cast: the render lifecycle and cast_scene_to_looking_glass,
174
+ # which this module's "Cast to LG" button now drives instead of open-coding
175
+ # the four-step build/render/write/cast itself. It subsumes [viz3d-render]
176
+ # (it declares pyvista too), but both are named so the dependency reads as
177
+ # what it is rather than as a side effect.
178
+ "kgmodule-utils[viz3d-render,viz3d-qt]>=0.18.0",
158
179
  "pyvistaqt>=0.11.0",
159
180
  "trame-vtk>=2.0.0",
160
181
  # Looking Glass quilts. 0.4.0 supplies depth_report() and widened
161
- # requires-python to <3.14, which retires the old marker gate.
162
- "quiltwright>=0.4.0",
182
+ # requires-python to <3.14, which retires the old marker gate. 0.6.0 adds
183
+ # QuiltSpec.scaled(), which replaces the tile-grid rounding this repo used
184
+ # to open-code, and save_and_cast_quilt() underneath the SDK cast helper.
185
+ "quiltwright>=0.8.0",
163
186
  ]
164
187
 
165
188
  # Cross-KG sibling packages (doc-kg, pycode-kg, agent-kg, kg-rag) are NOT
@@ -184,7 +207,7 @@ all = [
184
207
  "pyvis>=0.3.2",
185
208
  "pyvista>=0.44.0",
186
209
  "pyvistaqt>=0.11.0",
187
- "quiltwright>=0.4.0",
210
+ "quiltwright>=0.8.0",
188
211
  "streamlit>=1.56.0",
189
212
  "trame-vtk>=2.0.0",
190
213
  ]
@@ -226,7 +249,7 @@ ty = ">=0.0.41"
226
249
  optional = true
227
250
 
228
251
  [tool.poetry.group.kg.dependencies]
229
- doc-kg = ">=0.21.2"
252
+ doc-kg = ">=0.22.0"
230
253
 
231
254
  [project.urls]
232
255
  Homepage = "https://github.com/Flux-Frontiers/pycode_kg"
@@ -36,7 +36,7 @@ Author: Eric G. Suchanek, PhD
36
36
  License: Elastic 2.0
37
37
  """
38
38
 
39
- __version__ = "0.23.0"
39
+ __version__ = "0.24.0"
40
40
  __author__ = "Eric G. Suchanek, PhD"
41
41
 
42
42
  # Low-level primitives (locked v0 contract)
@@ -4,7 +4,6 @@ Measures module interaction complexity: how many unique modules each module call
4
4
  For well-modularized codebases, identifies orchestrator and hub modules.
5
5
 
6
6
  Author: Eric G. Suchanek, PhD
7
- Last Revision: 2026-08-14 20:59:39
8
7
  License: Elastic 2.0
9
8
  """
10
9
 
@@ -12,7 +12,6 @@ Public API:
12
12
  - :func:`aggregate_module_scores` — roll node scores up to module level.
13
13
 
14
14
  Author: Eric G. Suchanek, PhD
15
- Last Revision: 2026-08-14 22:45:27
16
15
 
17
16
  License: Elastic 2.0
18
17
  """
@@ -3,7 +3,6 @@ Framework Detector for PyCodeKG.
3
3
  Identifies repo-defining abstractions using centrality and cross-module signals.
4
4
 
5
5
  Author: Eric G. Suchanek, PhD
6
- Last Revision: 2026-08-14 20:59:39
7
6
  License: Elastic 2.0
8
7
  """
9
8
 
@@ -6,7 +6,6 @@ CLI command for installing PyCodeKG git hooks:
6
6
  install-hooks — install the pre-commit snapshot hook into .git/hooks/
7
7
 
8
8
  Author: Eric G. Suchanek, PhD
9
- Last Revision: 2026-08-11 19:32:52
10
9
 
11
10
  Author: Eric G. Suchanek, PhD
12
11
 
@@ -29,10 +28,33 @@ from pycode_kg.cli.main import cli
29
28
 
30
29
  _PRE_COMMIT_HOOK = """\
31
30
  #!/usr/bin/env bash
32
- # PyCodeKG pre-commit hook — runs quality checks first, then rebuilds the local
33
- # index and captures a metrics snapshot.
31
+ # PyCodeKG pre-commit hook — runs quality checks. The index rebuild and metrics
32
+ # snapshot are opt-in and OFF by default; see "Why snapshots are off" below.
34
33
  # Installed by: pycodekg install-hooks
35
- # Skip with: PYCODEKG_SKIP_SNAPSHOT=1 git commit ...
34
+ #
35
+ # PYCODEKG_SNAPSHOT=1 git commit ... opt in to a per-commit snapshot
36
+ # PYCODEKG_SKIP_SNAPSHOT=1 git commit ... force snapshots off (wins)
37
+ #
38
+ # Note that PYCODEKG_SKIP_SNAPSHOT no longer skips the quality checks. It used
39
+ # to short-circuit the whole hook, so a variable named "skip snapshot" also
40
+ # silently skipped ruff, ty and pytest. It now gates only what it names.
41
+ #
42
+ # Why snapshots are off by default (2026-08-18)
43
+ # ---------------------------------------------
44
+ # A per-commit snapshot records `git write-tree` and is then itself staged into
45
+ # that same commit. Staging changes the index, so the recorded hash can never
46
+ # equal the tree it claims to describe — and manifest.json carries a
47
+ # `last_update` timestamp, so the `git add` is never a no-op. The drift is
48
+ # guaranteed by construction, not caused by formatting, and reordering does not
49
+ # fix it: the `git add` lands after the hash either way.
50
+ #
51
+ # An audit of 605 snapshots across 29 fleet manifests found 63 (10.4%) keyed to
52
+ # a tree any commit actually has. `snapshot diff` between adjacent entries has
53
+ # therefore been comparing states that never existed.
54
+ #
55
+ # The fix is to snapshot at release, keyed on the tag rather than on an
56
+ # ephemeral pre-commit tree. See kgrag_priv/docs/SNAPSHOT_STRATEGY.md. Until
57
+ # that lands, this hook runs quality checks only.
36
58
  #
37
59
  # Order matters, and it is deliberately checks-then-index:
38
60
  #
@@ -46,8 +68,6 @@ _PRE_COMMIT_HOOK = """\
46
68
  # commit that ruff/ty/pytest is about to reject.
47
69
  set -euo pipefail
48
70
 
49
- [ "${PYCODEKG_SKIP_SNAPSHOT:-0}" = "1" ] && exit 0
50
-
51
71
  REPO_ROOT="$(git rev-parse --show-toplevel)"
52
72
 
53
73
  cd "$REPO_ROOT"
@@ -63,12 +83,27 @@ elif command -v pre-commit &>/dev/null; then
63
83
  pre-commit run || exit 1
64
84
  fi
65
85
 
66
- # Capture the tree hash now that the checks have passed and nothing further
67
- # will modify the working tree — this keys the snapshot to the content that is
68
- # actually about to be committed.
86
+ # ---------------------------------------------------------------------------
87
+ # Opt-in index rebuild + snapshot. Everything below is skipped unless
88
+ # PYCODEKG_SNAPSHOT=1 is set, and is skipped regardless if
89
+ # PYCODEKG_SKIP_SNAPSHOT=1.
90
+ # ---------------------------------------------------------------------------
91
+ [ "${PYCODEKG_SNAPSHOT:-0}" = "1" ] || exit 0
92
+ [ "${PYCODEKG_SKIP_SNAPSHOT:-0}" = "1" ] && exit 0
93
+
94
+ # Captured after the checks so nothing further modifies the working tree. Note
95
+ # the caveat above: this still cannot match the committed tree, because the
96
+ # `git add` below changes the index after this point.
69
97
  TREE_HASH=$(git write-tree)
70
98
  BRANCH=$(git rev-parse --abbrev-ref HEAD)
71
99
 
100
+ # Snapshots are default-branch history. On any other branch, stop here —
101
+ # feature-branch commits (and the PRs/CI built from them) stay free of
102
+ # generated snapshot files, and skip the 60-90s rebuild along the way.
103
+ DEFAULT_BRANCH=$(git symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null || true)
104
+ DEFAULT_BRANCH="${DEFAULT_BRANCH#origin/}"
105
+ [ "$BRANCH" != "${DEFAULT_BRANCH:-main}" ] && exit 0
106
+
72
107
  # Rebuild the local index to keep it in sync with staged content.
73
108
  "$REPO_ROOT/.venv/bin/pycodekg" build --repo "$REPO_ROOT" || exit 1
74
109
 
@@ -104,11 +139,14 @@ exit 0
104
139
  def install_hooks(repo: str, force: bool) -> None:
105
140
  """Install the PyCodeKG pre-commit git hook.
106
141
 
107
- After installation, before each commit:
142
+ After installation, before each commit on the default branch:
108
143
  1. Rebuilds the local PyCodeKG index (full wipe)
109
144
  2. Captures a metrics snapshot, keyed by tree hash
110
145
  3. Stages the snapshot directory
111
146
 
147
+ On any other branch the quality checks still run but the rebuild and
148
+ snapshot are skipped, so feature-branch commits carry no generated files.
149
+
112
150
  This keeps the index in sync and ensures snapshots reflect the state of
113
151
  the knowledge graph at commit time. The hook only ever touches its own
114
152
  repository — sibling KG repos install their own hooks.
@@ -137,5 +175,8 @@ def install_hooks(repo: str, force: bool) -> None:
137
175
  hook_path.chmod(mode)
138
176
 
139
177
  click.echo(f"OK Installed pre-commit hook: {hook_path}")
140
- click.echo(" Snapshots will be captured automatically before each commit.")
178
+ click.echo(" Quality checks run on every commit.")
179
+ click.echo(" Snapshots are OFF by default - see kgrag_priv/docs/SNAPSHOT_STRATEGY.md.")
180
+ click.echo(" Opt in with: PYCODEKG_SNAPSHOT=1 git commit ...")
181
+ click.echo(" Force off: PYCODEKG_SKIP_SNAPSHOT=1 git commit ...")
141
182
  click.echo(" Run 'pycodekg build' first if you haven't built the graph yet.")
@@ -253,6 +253,8 @@ def init(
253
253
 
254
254
  docstring_cov = analysis.get("docstring_coverage", {})
255
255
  coverage = docstring_cov.get("coverage_pct", 0.0) / 100.0 if docstring_cov else 0.0
256
+ coverage_documented = docstring_cov.get("with_doc", 0)
257
+ coverage_total = docstring_cov.get("total", 0)
256
258
  critical_issues = len(analysis.get("issues", []))
257
259
 
258
260
  fn_metrics = analysis.get("function_metrics", {})
@@ -276,6 +278,8 @@ def init(
276
278
  branch=branch,
277
279
  graph_stats_dict=stats,
278
280
  coverage=coverage,
281
+ coverage_documented=coverage_documented,
282
+ coverage_total=coverage_total,
279
283
  critical_issues=critical_issues,
280
284
  complexity_median=complexity_median,
281
285
  hotspots=hotspots,
@@ -12,7 +12,6 @@ in :mod:`quiltwright`. What this command owns is the part quiltwright cannot
12
12
  know: which graph to grow and how to frame it.
13
13
 
14
14
  Author: Eric G. Suchanek, PhD
15
- Last Revision: 2026-08-14
16
15
 
17
16
  License: Elastic 2.0
18
17
  """
@@ -111,6 +111,8 @@ def save_snapshot(
111
111
  # Extract metrics from analysis results
112
112
  docstring_cov = analysis.get("docstring_coverage", {})
113
113
  coverage = docstring_cov.get("coverage_pct", 0.0) / 100.0 if docstring_cov else 0.0
114
+ coverage_documented = docstring_cov.get("with_doc", 0)
115
+ coverage_total = docstring_cov.get("total", 0)
114
116
  issue_strings = analysis.get("issues", [])
115
117
  critical_issues = len(issue_strings)
116
118
 
@@ -140,6 +142,8 @@ def save_snapshot(
140
142
  branch=branch,
141
143
  graph_stats_dict=stats,
142
144
  coverage=coverage,
145
+ coverage_documented=coverage_documented,
146
+ coverage_total=coverage_total,
143
147
  critical_issues=critical_issues,
144
148
  complexity_median=complexity_median,
145
149
  hotspots=hotspots,
@@ -23,7 +23,6 @@ No persistence, no embeddings, no LLMs—just pure AST extraction. Integration w
23
23
  vector databases and semantic search happens downstream.
24
24
 
25
25
  Author: Eric G. Suchanek, PhD
26
- Last Revision: 2026-08-14 22:45:27
27
26
 
28
27
  License: Elastic 2.0
29
28
  """