tscode-kg 0.2.0__tar.gz → 0.4.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.
- tscode_kg-0.4.0/LICENSE +94 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/PKG-INFO +41 -18
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/README.md +39 -4
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/pyproject.toml +74 -24
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/__init__.py +1 -1
- tscode_kg-0.4.0/src/tscode_kg/cli/cmd_build.py +229 -0
- tscode_kg-0.4.0/src/tscode_kg/cli/cmd_hooks.py +172 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/cli/cmd_snapshot.py +29 -8
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/cli/main.py +4 -1
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/snapshots.py +8 -4
- tscode_kg-0.2.0/LICENSE +0 -24
- tscode_kg-0.2.0/src/tscode_kg/cli/cmd_build.py +0 -86
- tscode_kg-0.2.0/src/tscode_kg/cli/cmd_hooks.py +0 -125
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/__main__.py +0 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/analysis.py +0 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/app.py +0 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/bridge.py +0 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/centrality.py +0 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/cli/__init__.py +0 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/cli/cmd_analyze.py +0 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/cli/cmd_bridges.py +0 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/cli/cmd_centrality.py +0 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/cli/cmd_explain.py +0 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/cli/cmd_framework_nodes.py +0 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/cli/cmd_init.py +0 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/cli/cmd_mcp.py +0 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/cli/cmd_model.py +0 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/cli/cmd_query.py +0 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/cli/cmd_viz.py +0 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/coderank.py +0 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/config.py +0 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/explain.py +0 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/extractor.py +0 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/framework_detector.py +0 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/kg.py +0 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/layout3d.py +0 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/mcp_server.py +0 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/viz3d.py +0 -0
- {tscode_kg-0.2.0 → tscode_kg-0.4.0}/src/tscode_kg/viz3d_timeline.py +0 -0
tscode_kg-0.4.0/LICENSE
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
Elastic License 2.0
|
|
2
|
+
|
|
3
|
+
URL: https://www.elastic.co/licensing/elastic-license
|
|
4
|
+
|
|
5
|
+
## Acceptance
|
|
6
|
+
|
|
7
|
+
By using the software, you agree to all of the terms and conditions below.
|
|
8
|
+
|
|
9
|
+
## Copyright License
|
|
10
|
+
|
|
11
|
+
The licensor grants you a non-exclusive, royalty-free, worldwide,
|
|
12
|
+
non-sublicensable, non-transferable license to use, copy, distribute, make
|
|
13
|
+
available, and prepare derivative works of the software, in each case subject to
|
|
14
|
+
the limitations and conditions below.
|
|
15
|
+
|
|
16
|
+
## Limitations
|
|
17
|
+
|
|
18
|
+
**You may not provide the software to third parties as a hosted or managed
|
|
19
|
+
service, where the service provides users with access to any substantial set of
|
|
20
|
+
the features or functionality of the software.**
|
|
21
|
+
|
|
22
|
+
You may not move, change, disable, or circumvent the license key functionality
|
|
23
|
+
in the software, and you may not remove or obscure any functionality in the
|
|
24
|
+
software that is protected by the license key.
|
|
25
|
+
|
|
26
|
+
You may not alter, remove, or obscure any licensing, copyright, or other notices
|
|
27
|
+
of the licensor in the software. Any use of the licensor's trademarks is subject
|
|
28
|
+
to applicable law.
|
|
29
|
+
|
|
30
|
+
## Patents
|
|
31
|
+
|
|
32
|
+
The licensor grants you a license, under any patent claims the licensor can
|
|
33
|
+
license, or becomes able to license, to make, have made, use, sell, offer for
|
|
34
|
+
sale, import and have imported the software, in each case subject to the
|
|
35
|
+
limitations and conditions in this license. This license does not cover any
|
|
36
|
+
patent claims that you cause to be infringed by modifications or additions to the
|
|
37
|
+
software. If you or your company make any written claim that the software
|
|
38
|
+
infringes or contributes to infringement of any patent, your patent license for
|
|
39
|
+
the software granted under these terms ends immediately. If your company makes
|
|
40
|
+
such a claim, your patent license ends immediately for work on behalf of your
|
|
41
|
+
company.
|
|
42
|
+
|
|
43
|
+
## Notices
|
|
44
|
+
|
|
45
|
+
You must ensure that anyone who gets a copy of any part of the software from you
|
|
46
|
+
also gets a copy of these terms or the URL for them above, as well as copies of
|
|
47
|
+
any plain-text lines beginning with "Required Notice:" that the licensor provided
|
|
48
|
+
with the software. For example:
|
|
49
|
+
|
|
50
|
+
Required Notice: Copyright (c) 2026 Eric G. Suchanek, PhD
|
|
51
|
+
|
|
52
|
+
## No Other Rights
|
|
53
|
+
|
|
54
|
+
These terms do not imply any other licenses not expressly granted in this
|
|
55
|
+
license.
|
|
56
|
+
|
|
57
|
+
## Termination
|
|
58
|
+
|
|
59
|
+
If you use the software in violation of these terms, such use is not licensed,
|
|
60
|
+
and your licenses will automatically terminate. If the licensor provides you with
|
|
61
|
+
a notice of your violation, and you cease all violation of this license no later
|
|
62
|
+
than 30 days after you receive that notice, your licenses will be reinstated
|
|
63
|
+
retroactively. However, if you violate these terms after such reinstatement, any
|
|
64
|
+
additional violation of these terms will cause your licenses to terminate
|
|
65
|
+
automatically and permanently.
|
|
66
|
+
|
|
67
|
+
## No Liability
|
|
68
|
+
|
|
69
|
+
*As far as the law allows, the software comes as is, without any warranty or
|
|
70
|
+
condition, and the licensor will not be liable to you for any damages arising out
|
|
71
|
+
of these terms or the use or nature of the software, under any kind of legal
|
|
72
|
+
claim.*
|
|
73
|
+
|
|
74
|
+
## Definitions
|
|
75
|
+
|
|
76
|
+
The **licensor** is the entity offering these terms, and the **software** is the
|
|
77
|
+
software the licensor makes available under these terms, including any portion of
|
|
78
|
+
it.
|
|
79
|
+
|
|
80
|
+
**You** refers to the individual or entity agreeing to these terms.
|
|
81
|
+
|
|
82
|
+
**Your company** is any legal entity, sole proprietorship, or other kind of
|
|
83
|
+
organization that you work for, plus all organizations that have control over,
|
|
84
|
+
are under the control of, or are under common control with that organization.
|
|
85
|
+
Control means ownership of substantially all the assets of an entity, or the
|
|
86
|
+
power to direct its management and policies by vote, contract, or otherwise.
|
|
87
|
+
Control can be direct or indirect.
|
|
88
|
+
|
|
89
|
+
**Your licenses** are all the licenses granted to you for the software under
|
|
90
|
+
these terms.
|
|
91
|
+
|
|
92
|
+
**Use** means anything you do with the software requiring one of your licenses.
|
|
93
|
+
|
|
94
|
+
**Trademark** means trademarks, service marks, and similar rights.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tscode-kg
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Knowledge graph for TypeScript/JavaScript codebases — AST extraction, hybrid semantic+structural search
|
|
5
5
|
License-Expression: Elastic-2.0
|
|
6
6
|
License-File: LICENSE
|
|
@@ -15,42 +15,38 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
-
Provides-Extra: dev
|
|
19
|
-
Provides-Extra: kgdeps
|
|
20
18
|
Provides-Extra: viz
|
|
21
19
|
Provides-Extra: viz3d
|
|
22
20
|
Requires-Dist: PyQt5 (>=5.15.11,<5.16) ; extra == "viz3d"
|
|
23
21
|
Requires-Dist: click (>=8.1.0,<9)
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Requires-Dist: doc-kg (>=0.18.1) ; extra == "kgdeps"
|
|
26
|
-
Requires-Dist: kgmodule-utils[semantic,sqlite-vec] (>=0.8.0)
|
|
22
|
+
Requires-Dist: kgmodule-utils[semantic,sqlite-vec] (>=0.19.1)
|
|
27
23
|
Requires-Dist: markdown (>=3.10,<3.11) ; extra == "viz3d"
|
|
28
24
|
Requires-Dist: mcp (>=1.0.0,<2)
|
|
29
25
|
Requires-Dist: networkx (>=3.0)
|
|
30
26
|
Requires-Dist: param (>=2.4,<2.5) ; extra == "viz3d"
|
|
31
27
|
Requires-Dist: plotly (>=6.8.0,<6.9) ; extra == "viz"
|
|
32
|
-
Requires-Dist: pre-commit (>=4.5.1) ; extra == "dev"
|
|
33
|
-
Requires-Dist: pycode-kg (>=0.20.0,<0.21) ; extra == "dev"
|
|
34
|
-
Requires-Dist: pycode-kg (>=0.20.0,<0.21) ; extra == "kgdeps"
|
|
35
|
-
Requires-Dist: pylint (>=4.0.5) ; extra == "dev"
|
|
36
|
-
Requires-Dist: pytest (>=8.0.0) ; extra == "dev"
|
|
37
|
-
Requires-Dist: pytest-cov (>=5.0.0) ; extra == "dev"
|
|
38
28
|
Requires-Dist: pyvis (>=0.3.2,<0.4) ; extra == "viz"
|
|
39
29
|
Requires-Dist: pyvista (>=0.48.4,<0.49) ; extra == "viz3d"
|
|
40
30
|
Requires-Dist: pyvistaqt (>=0.12,<0.13) ; extra == "viz3d"
|
|
41
31
|
Requires-Dist: rich (>=14.3.3,<15)
|
|
42
|
-
Requires-Dist: ruff (>=0.4.0) ; extra == "dev"
|
|
43
32
|
Requires-Dist: streamlit (>=1.59.0,<1.60) ; extra == "viz"
|
|
44
33
|
Requires-Dist: trame-vtk (>=2.11.8,<2.12) ; extra == "viz3d"
|
|
45
34
|
Requires-Dist: tree-sitter (>=0.25.0)
|
|
46
35
|
Requires-Dist: tree-sitter-typescript (>=0.23.2)
|
|
47
|
-
Requires-Dist: ty (>=0.0.41) ; extra == "dev"
|
|
48
36
|
Project-URL: Homepage, https://github.com/Flux-Frontiers/tscode_kg
|
|
49
37
|
Project-URL: Repository, https://github.com/Flux-Frontiers/tscode_kg
|
|
50
38
|
Description-Content-Type: text/markdown
|
|
51
39
|
|
|
52
40
|
# TypeScriptKG
|
|
53
41
|
|
|
42
|
+
[](https://www.python.org/)
|
|
43
|
+
[](https://www.elastic.co/licensing/elastic-license)
|
|
44
|
+
[](https://pypi.org/project/tscode-kg/)
|
|
45
|
+
[](https://github.com/Flux-Frontiers/tscode_kg/releases)
|
|
46
|
+
[](https://github.com/Flux-Frontiers/tscode_kg/actions/workflows/ci.yml)
|
|
47
|
+
[](https://python-poetry.org/)
|
|
48
|
+
[](https://zenodo.org/badge/latestdoi/1247715028)
|
|
49
|
+
|
|
54
50
|
Knowledge graph for TypeScript and JavaScript codebases — deterministic AST extraction, hybrid semantic + structural search.
|
|
55
51
|
|
|
56
52
|
## Overview
|
|
@@ -112,7 +108,7 @@ tscodekg framework-nodes --top 20
|
|
|
112
108
|
tscodekg explain "fn:src/utils/helpers.ts:formatDate"
|
|
113
109
|
|
|
114
110
|
# Temporal metric snapshots
|
|
115
|
-
tscodekg snapshot save --repo /path/to/ts-repo
|
|
111
|
+
tscodekg snapshot save 0.4.0 --repo /path/to/ts-repo --subject repo:tscode-kg
|
|
116
112
|
tscodekg snapshot list
|
|
117
113
|
|
|
118
114
|
# Interactive visualizers (install extras: tscode-kg[viz] / tscode-kg[viz3d])
|
|
@@ -146,9 +142,12 @@ Repo-local Claude Code skills live in `skills/`.
|
|
|
146
142
|
|
|
147
143
|
## Snapshots & git hook
|
|
148
144
|
|
|
149
|
-
`tscodekg snapshot save` captures graph metrics (nodes, edges,
|
|
150
|
-
issues, hotspots)
|
|
151
|
-
|
|
145
|
+
`tscodekg snapshot save [VERSION]` captures graph metrics (nodes, edges,
|
|
146
|
+
JSDoc coverage, issues, hotspots) into `.tscodekg/snapshots/`, keyed on
|
|
147
|
+
VERSION -- pass a release tag at release time, or omit it for a UTC
|
|
148
|
+
timestamp key, which is what the per-commit hook below uses. The git tree
|
|
149
|
+
hash is recorded as provenance, not the key. Deltas are computed against
|
|
150
|
+
the previous and baseline snapshots.
|
|
152
151
|
`tscodekg install-hooks` installs a pre-commit hook that rebuilds the index,
|
|
153
152
|
captures a snapshot, stages the snapshot directory, and then runs the
|
|
154
153
|
pre-commit framework checks — so every commit records the state of the
|
|
@@ -190,6 +189,30 @@ exclude = ["__tests__"] # extra dirs to skip
|
|
|
190
189
|
|
|
191
190
|
Eric G. Suchanek, PhD — Flux Frontiers
|
|
192
191
|
|
|
192
|
+
## Citation
|
|
193
|
+
|
|
194
|
+
If you use TypeScriptKG in your research or project, please cite it:
|
|
195
|
+
|
|
196
|
+
[](https://zenodo.org/badge/latestdoi/1247715028)
|
|
197
|
+
|
|
198
|
+
> Suchanek, E. G. (2026). *TypeScriptKG: Semantic Knowledge Graph for TypeScript/JavaScript Codebases* (Version 0.4.0) [Software]. Flux-Frontiers. https://github.com/Flux-Frontiers/tscode_kg
|
|
199
|
+
|
|
200
|
+
```bibtex
|
|
201
|
+
@software{suchanek_tscode_kg,
|
|
202
|
+
author = {Suchanek, Eric G.},
|
|
203
|
+
title = {{TypeScriptKG}: Semantic Knowledge Graph for TypeScript/JavaScript Codebases},
|
|
204
|
+
version = {0.4.0},
|
|
205
|
+
year = {2026},
|
|
206
|
+
publisher = {Flux-Frontiers},
|
|
207
|
+
url = {https://github.com/Flux-Frontiers/tscode_kg},
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Full citation metadata in [`CITATION.cff`](CITATION.cff). This is the first
|
|
212
|
+
release archived by Zenodo; the DOI resolves via the badge above once the
|
|
213
|
+
archive completes, and this section is updated with the concept DOI directly
|
|
214
|
+
afterward.
|
|
215
|
+
|
|
193
216
|
## License
|
|
194
217
|
|
|
195
218
|
Elastic-2.0
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# TypeScriptKG
|
|
2
2
|
|
|
3
|
+
[](https://www.python.org/)
|
|
4
|
+
[](https://www.elastic.co/licensing/elastic-license)
|
|
5
|
+
[](https://pypi.org/project/tscode-kg/)
|
|
6
|
+
[](https://github.com/Flux-Frontiers/tscode_kg/releases)
|
|
7
|
+
[](https://github.com/Flux-Frontiers/tscode_kg/actions/workflows/ci.yml)
|
|
8
|
+
[](https://python-poetry.org/)
|
|
9
|
+
[](https://zenodo.org/badge/latestdoi/1247715028)
|
|
10
|
+
|
|
3
11
|
Knowledge graph for TypeScript and JavaScript codebases — deterministic AST extraction, hybrid semantic + structural search.
|
|
4
12
|
|
|
5
13
|
## Overview
|
|
@@ -61,7 +69,7 @@ tscodekg framework-nodes --top 20
|
|
|
61
69
|
tscodekg explain "fn:src/utils/helpers.ts:formatDate"
|
|
62
70
|
|
|
63
71
|
# Temporal metric snapshots
|
|
64
|
-
tscodekg snapshot save --repo /path/to/ts-repo
|
|
72
|
+
tscodekg snapshot save 0.4.0 --repo /path/to/ts-repo --subject repo:tscode-kg
|
|
65
73
|
tscodekg snapshot list
|
|
66
74
|
|
|
67
75
|
# Interactive visualizers (install extras: tscode-kg[viz] / tscode-kg[viz3d])
|
|
@@ -95,9 +103,12 @@ Repo-local Claude Code skills live in `skills/`.
|
|
|
95
103
|
|
|
96
104
|
## Snapshots & git hook
|
|
97
105
|
|
|
98
|
-
`tscodekg snapshot save` captures graph metrics (nodes, edges,
|
|
99
|
-
issues, hotspots)
|
|
100
|
-
|
|
106
|
+
`tscodekg snapshot save [VERSION]` captures graph metrics (nodes, edges,
|
|
107
|
+
JSDoc coverage, issues, hotspots) into `.tscodekg/snapshots/`, keyed on
|
|
108
|
+
VERSION -- pass a release tag at release time, or omit it for a UTC
|
|
109
|
+
timestamp key, which is what the per-commit hook below uses. The git tree
|
|
110
|
+
hash is recorded as provenance, not the key. Deltas are computed against
|
|
111
|
+
the previous and baseline snapshots.
|
|
101
112
|
`tscodekg install-hooks` installs a pre-commit hook that rebuilds the index,
|
|
102
113
|
captures a snapshot, stages the snapshot directory, and then runs the
|
|
103
114
|
pre-commit framework checks — so every commit records the state of the
|
|
@@ -139,6 +150,30 @@ exclude = ["__tests__"] # extra dirs to skip
|
|
|
139
150
|
|
|
140
151
|
Eric G. Suchanek, PhD — Flux Frontiers
|
|
141
152
|
|
|
153
|
+
## Citation
|
|
154
|
+
|
|
155
|
+
If you use TypeScriptKG in your research or project, please cite it:
|
|
156
|
+
|
|
157
|
+
[](https://zenodo.org/badge/latestdoi/1247715028)
|
|
158
|
+
|
|
159
|
+
> Suchanek, E. G. (2026). *TypeScriptKG: Semantic Knowledge Graph for TypeScript/JavaScript Codebases* (Version 0.4.0) [Software]. Flux-Frontiers. https://github.com/Flux-Frontiers/tscode_kg
|
|
160
|
+
|
|
161
|
+
```bibtex
|
|
162
|
+
@software{suchanek_tscode_kg,
|
|
163
|
+
author = {Suchanek, Eric G.},
|
|
164
|
+
title = {{TypeScriptKG}: Semantic Knowledge Graph for TypeScript/JavaScript Codebases},
|
|
165
|
+
version = {0.4.0},
|
|
166
|
+
year = {2026},
|
|
167
|
+
publisher = {Flux-Frontiers},
|
|
168
|
+
url = {https://github.com/Flux-Frontiers/tscode_kg},
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Full citation metadata in [`CITATION.cff`](CITATION.cff). This is the first
|
|
173
|
+
release archived by Zenodo; the DOI resolves via the badge above once the
|
|
174
|
+
archive completes, and this section is updated with the concept DOI directly
|
|
175
|
+
afterward.
|
|
176
|
+
|
|
142
177
|
## License
|
|
143
178
|
|
|
144
179
|
Elastic-2.0
|
|
@@ -4,20 +4,30 @@
|
|
|
4
4
|
#
|
|
5
5
|
# Build system : Poetry 2.x with PEP 621 [project] table
|
|
6
6
|
#
|
|
7
|
+
# Extras are user-facing features; dev tooling is a Poetry group, so it never
|
|
8
|
+
# ships in the wheel and cannot be pip-installed. See FLEET_STANDARDS.md.
|
|
9
|
+
#
|
|
7
10
|
# Quick install (pip)
|
|
8
11
|
# -------------------
|
|
9
|
-
# pip install -e ".
|
|
10
|
-
# pip install -e "."
|
|
12
|
+
# pip install -e "." core runtime only
|
|
13
|
+
# pip install -e ".[viz]" + Streamlit/Plotly explorer
|
|
14
|
+
# pip install -e ".[viz3d]" + PyVista/PyQt5 3-D visualizer
|
|
15
|
+
#
|
|
16
|
+
# Quick install (poetry)
|
|
17
|
+
# ----------------------
|
|
18
|
+
# poetry install core runtime only
|
|
19
|
+
# poetry install --with dev + pytest, ruff, ty, pre-commit
|
|
20
|
+
# poetry install --with kg + the pycodekg / dockg CLIs
|
|
21
|
+
# poetry install --all-extras --with dev,kg full maintainer environment
|
|
11
22
|
#
|
|
12
23
|
# First-time setup
|
|
13
24
|
# ----------------
|
|
14
|
-
#
|
|
15
|
-
# source .venv/bin/activate
|
|
16
|
-
# pip install -e ".[dev]"
|
|
25
|
+
# poetry install --with dev
|
|
17
26
|
#
|
|
18
27
|
# Build KG indices
|
|
19
28
|
# ----------------
|
|
20
|
-
# tscodekg build --repo .
|
|
29
|
+
# tscodekg build --repo . full rebuild (wipes)
|
|
30
|
+
# tscodekg update --repo . incremental upsert
|
|
21
31
|
|
|
22
32
|
[build-system]
|
|
23
33
|
requires = ["poetry-core>=2.0.0"]
|
|
@@ -28,7 +38,7 @@ packages = [{ include = "tscode_kg", from = "src" }]
|
|
|
28
38
|
|
|
29
39
|
[project]
|
|
30
40
|
name = "tscode-kg"
|
|
31
|
-
version = "0.
|
|
41
|
+
version = "0.4.0"
|
|
32
42
|
description = "Knowledge graph for TypeScript/JavaScript codebases — AST extraction, hybrid semantic+structural search"
|
|
33
43
|
readme = "README.md"
|
|
34
44
|
license = "Elastic-2.0"
|
|
@@ -54,7 +64,7 @@ dependencies = [
|
|
|
54
64
|
"tree-sitter>=0.25.0",
|
|
55
65
|
"tree-sitter-typescript>=0.23.2",
|
|
56
66
|
"rich>=14.3.3,<15",
|
|
57
|
-
"kgmodule-utils[semantic,sqlite-vec]>=0.
|
|
67
|
+
"kgmodule-utils[semantic,sqlite-vec]>=0.19.1",
|
|
58
68
|
"click>=8.1.0,<9",
|
|
59
69
|
# Upper-bounded: mcp 2.0 removed the bundled `mcp.server.fastmcp` module
|
|
60
70
|
# (FastMCP was split out into the standalone `fastmcp` package) and rebuilt
|
|
@@ -67,11 +77,9 @@ dependencies = [
|
|
|
67
77
|
]
|
|
68
78
|
|
|
69
79
|
[project.optional-dependencies]
|
|
70
|
-
#
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
"doc-kg>=0.18.1",
|
|
74
|
-
]
|
|
80
|
+
# pycode-kg is not an extra: it is never imported by this package, so it does
|
|
81
|
+
# not belong in published metadata. It lives in [tool.poetry.group.kg] below --
|
|
82
|
+
# see that block for why the old "install it by hand" workaround is retired.
|
|
75
83
|
# Streamlit graph explorer + plotly timeline.
|
|
76
84
|
# Version brackets follow pycode_kg's proven lockfile versions.
|
|
77
85
|
viz = [
|
|
@@ -93,17 +101,56 @@ viz3d = [
|
|
|
93
101
|
"pyvistaqt>=0.12,<0.13",
|
|
94
102
|
"trame-vtk>=2.11.8,<2.12",
|
|
95
103
|
]
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
|
|
105
|
+
# ---------------------------------------------------------------------------
|
|
106
|
+
# Dev tooling (Poetry group, not a PEP 621 extra)
|
|
107
|
+
# ---------------------------------------------------------------------------
|
|
108
|
+
# `optional = true` keeps it out of a bare `poetry install`.
|
|
109
|
+
[tool.poetry.group.dev]
|
|
110
|
+
optional = true
|
|
111
|
+
|
|
112
|
+
[tool.poetry.group.dev.dependencies]
|
|
113
|
+
detect-secrets = ">=1.5.0"
|
|
114
|
+
ty = ">=0.0.44"
|
|
115
|
+
pre-commit = ">=4.5.1"
|
|
116
|
+
# >=9.0.3 is a security floor, not a preference: GHSA-6w46-j5rx-g56g.
|
|
117
|
+
pytest = ">=9.0.3"
|
|
118
|
+
pytest-cov = ">=5.0.0"
|
|
119
|
+
# Capped below 0.16 to match the fleet: 0.16 formats Python inside Markdown
|
|
120
|
+
# code blocks as stable behaviour. The floor was unbounded, so the lock could
|
|
121
|
+
# drift away from the version .pre-commit-config.yaml pins.
|
|
122
|
+
ruff = ">=0.4.0,<0.16"
|
|
123
|
+
|
|
124
|
+
# ---------------------------------------------------------------------------
|
|
125
|
+
# Maintainer tooling (Poetry group, not a PEP 621 extra)
|
|
126
|
+
# ---------------------------------------------------------------------------
|
|
127
|
+
# pycode-kg is a tool this repo *runs*, not a library it imports: PyCodeKG
|
|
128
|
+
# indexes this repo's Python source from the outside, via the `pycodekg` CLI in
|
|
129
|
+
# .git/hooks/pre-commit. It belongs in a Poetry group -- locked and installable,
|
|
130
|
+
# but never written into the wheel, so no published extra acquires a sibling.
|
|
131
|
+
#
|
|
132
|
+
# doc-kg rides along for fleet uniformity: every repo's `kg` group carries the
|
|
133
|
+
# same two CLIs, so `poetry install --with kg` means one thing everywhere. This
|
|
134
|
+
# repo has no DocKG index today (no .mcp.json, no `dockg` invocation), so the
|
|
135
|
+
# CLI is simply available rather than required -- and it is here, not in an
|
|
136
|
+
# extra, precisely because nothing depends on it at runtime.
|
|
137
|
+
#
|
|
138
|
+
# poetry install --with kg # get the pycodekg CLI into .venv/bin
|
|
139
|
+
# poetry install # default: group is optional, skipped
|
|
140
|
+
#
|
|
141
|
+
# This supersedes the standing "install it by hand" workaround. That note
|
|
142
|
+
# described a real deadlock -- older pycode-kg capped transformers below 4.57
|
|
143
|
+
# while kgmodule-utils >=0.9.0 needs >=5.5.0, which silently held this repo on
|
|
144
|
+
# the pre-CVE transformers line. pycode-kg 0.21.4 no longer pins transformers at
|
|
145
|
+
# all; it inherits the same kgmodule-utils[semantic] constraint this project
|
|
146
|
+
# already uses, so the two no longer conflict. Verified: locking with this group
|
|
147
|
+
# keeps transformers at 5.14.1.
|
|
148
|
+
[tool.poetry.group.kg]
|
|
149
|
+
optional = true
|
|
150
|
+
|
|
151
|
+
[tool.poetry.group.kg.dependencies]
|
|
152
|
+
doc-kg = ">=0.24.1"
|
|
153
|
+
pycode-kg = ">=0.26.0"
|
|
107
154
|
|
|
108
155
|
[project.urls]
|
|
109
156
|
Homepage = "https://github.com/Flux-Frontiers/tscode_kg"
|
|
@@ -113,6 +160,9 @@ Repository = "https://github.com/Flux-Frontiers/tscode_kg"
|
|
|
113
160
|
tscodekg = "tscode_kg.cli.main:cli"
|
|
114
161
|
tscodekg-analyze = "tscode_kg.cli.cmd_analyze:analyze"
|
|
115
162
|
tscodekg-build = "tscode_kg.cli.cmd_build:build"
|
|
163
|
+
tscodekg-update = "tscode_kg.cli.cmd_build:update"
|
|
164
|
+
tscodekg-build-sqlite = "tscode_kg.cli.cmd_build:build_sqlite"
|
|
165
|
+
tscodekg-build-index = "tscode_kg.cli.cmd_build:build_index"
|
|
116
166
|
tscodekg-centrality = "tscode_kg.cli.cmd_centrality:centrality"
|
|
117
167
|
tscodekg-download-model = "tscode_kg.cli.cmd_model:download_model"
|
|
118
168
|
tscodekg-init = "tscode_kg.cli.cmd_init:init"
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"""
|
|
2
|
+
cli/cmd_build.py — tscodekg build / update commands.
|
|
3
|
+
|
|
4
|
+
Builds the SQLite graph and sqlite-vec vector index from a TypeScript/JS repo.
|
|
5
|
+
|
|
6
|
+
Two commands, not one command with a flag, mirroring ``pycodekg``:
|
|
7
|
+
|
|
8
|
+
* ``build`` wipes existing data and rebuilds from scratch.
|
|
9
|
+
* ``update`` upserts changes without wiping.
|
|
10
|
+
|
|
11
|
+
The split matters because the two are different operations, not a switch on
|
|
12
|
+
one. A rebuild is correct after renames or deletions, where an upsert leaves
|
|
13
|
+
phantom nodes behind — the vector store upserts by node ID, so a renamed
|
|
14
|
+
symbol keeps its old entry forever. Making the safe operation the bare verb
|
|
15
|
+
means the surprising outcome has to be asked for by name.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
import click
|
|
23
|
+
from rich.console import Console
|
|
24
|
+
|
|
25
|
+
console = Console()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _build_options(fn):
|
|
29
|
+
"""Apply the option set shared by ``build`` and ``update``."""
|
|
30
|
+
for option in reversed(
|
|
31
|
+
[
|
|
32
|
+
click.option(
|
|
33
|
+
"--repo", default=".", show_default=True, help="Repository root directory."
|
|
34
|
+
),
|
|
35
|
+
click.option(
|
|
36
|
+
"--db",
|
|
37
|
+
default=None,
|
|
38
|
+
help="SQLite database path (default: <repo>/.tscodekg/graph.sqlite).",
|
|
39
|
+
),
|
|
40
|
+
click.option(
|
|
41
|
+
"--vectors",
|
|
42
|
+
default=None,
|
|
43
|
+
help="sqlite-vec store path (default: <repo>/.tscodekg/vectors.sqlite).",
|
|
44
|
+
),
|
|
45
|
+
click.option(
|
|
46
|
+
"--graph-only",
|
|
47
|
+
is_flag=True,
|
|
48
|
+
default=False,
|
|
49
|
+
help="Build SQLite graph only; skip vector index.",
|
|
50
|
+
),
|
|
51
|
+
click.option(
|
|
52
|
+
"--index-only",
|
|
53
|
+
is_flag=True,
|
|
54
|
+
default=False,
|
|
55
|
+
help="Build vector index only; graph must already exist.",
|
|
56
|
+
),
|
|
57
|
+
]
|
|
58
|
+
):
|
|
59
|
+
fn = option(fn)
|
|
60
|
+
return fn
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _run(
|
|
64
|
+
*,
|
|
65
|
+
repo: str,
|
|
66
|
+
db: str | None,
|
|
67
|
+
vectors: str | None,
|
|
68
|
+
graph_only: bool,
|
|
69
|
+
index_only: bool,
|
|
70
|
+
wipe: bool,
|
|
71
|
+
) -> None:
|
|
72
|
+
"""Shared body for ``build`` and ``update``."""
|
|
73
|
+
from tscode_kg.kg import TypeScriptKG # pylint: disable=import-outside-toplevel
|
|
74
|
+
|
|
75
|
+
repo_path = Path(repo).resolve()
|
|
76
|
+
if not repo_path.is_dir():
|
|
77
|
+
console.print(f"[red]Error:[/red] Repository not found: {repo_path}")
|
|
78
|
+
raise SystemExit(1)
|
|
79
|
+
|
|
80
|
+
kg = TypeScriptKG(
|
|
81
|
+
repo_root=repo_path,
|
|
82
|
+
db_path=db,
|
|
83
|
+
vectors_path=vectors,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
console.print(f"[bold]TypeScriptKG {'build' if wipe else 'update'}[/bold]")
|
|
87
|
+
console.print(f" repo : {repo_path}")
|
|
88
|
+
console.print(f" db : {kg.db_path}")
|
|
89
|
+
console.print(f" vectors : {kg.vectors_path}")
|
|
90
|
+
console.print(f" mode : {'full rebuild (wipes)' if wipe else 'incremental upsert'}")
|
|
91
|
+
console.print()
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
if index_only:
|
|
95
|
+
console.print("[cyan]Building vector index...[/cyan]")
|
|
96
|
+
stats = kg.build_index(wipe=wipe)
|
|
97
|
+
elif graph_only:
|
|
98
|
+
console.print("[cyan]Building SQLite graph...[/cyan]")
|
|
99
|
+
stats = kg.build_graph(wipe=wipe)
|
|
100
|
+
else:
|
|
101
|
+
console.print("[cyan]Building graph + vector index...[/cyan]")
|
|
102
|
+
stats = kg.build(wipe=wipe)
|
|
103
|
+
|
|
104
|
+
console.print("[green]Done.[/green]")
|
|
105
|
+
console.print(str(stats))
|
|
106
|
+
except Exception as exc: # pylint: disable=broad-except
|
|
107
|
+
console.print(f"[red]Build failed:[/red] {exc}")
|
|
108
|
+
raise SystemExit(1) from exc
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@click.command("build")
|
|
112
|
+
@_build_options
|
|
113
|
+
def build(
|
|
114
|
+
repo: str,
|
|
115
|
+
db: str | None,
|
|
116
|
+
vectors: str | None,
|
|
117
|
+
graph_only: bool,
|
|
118
|
+
index_only: bool,
|
|
119
|
+
) -> None:
|
|
120
|
+
"""Build knowledge graph from scratch: wipes existing data, then extracts
|
|
121
|
+
TypeScript/JS AST -> graph store -> vector index."""
|
|
122
|
+
_run(
|
|
123
|
+
repo=repo,
|
|
124
|
+
db=db,
|
|
125
|
+
vectors=vectors,
|
|
126
|
+
graph_only=graph_only,
|
|
127
|
+
index_only=index_only,
|
|
128
|
+
wipe=True,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@click.command("update")
|
|
133
|
+
@_build_options
|
|
134
|
+
def update(
|
|
135
|
+
repo: str,
|
|
136
|
+
db: str | None,
|
|
137
|
+
vectors: str | None,
|
|
138
|
+
graph_only: bool,
|
|
139
|
+
index_only: bool,
|
|
140
|
+
) -> None:
|
|
141
|
+
"""Update knowledge graph incrementally: upserts changes without wiping
|
|
142
|
+
existing data."""
|
|
143
|
+
_run(
|
|
144
|
+
repo=repo,
|
|
145
|
+
db=db,
|
|
146
|
+
vectors=vectors,
|
|
147
|
+
graph_only=graph_only,
|
|
148
|
+
index_only=index_only,
|
|
149
|
+
wipe=False,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
# ---------------------------------------------------------------------------
|
|
154
|
+
# Granular stages, mirroring `pycodekg build-sqlite` / `pycodekg build-index`
|
|
155
|
+
# ---------------------------------------------------------------------------
|
|
156
|
+
# These reach the same two halves as `build --graph-only` / `build --index-only`,
|
|
157
|
+
# exposed under the names pycodekg uses so the two CLIs read alike. Unlike
|
|
158
|
+
# `build`/`update` they keep `--wipe`: a stage is a lower-level tool than a
|
|
159
|
+
# verb, and pycodekg's stages carry the flag too.
|
|
160
|
+
#
|
|
161
|
+
# One deliberate divergence: pycodekg names this option `--db` on build-sqlite
|
|
162
|
+
# but `--sqlite` on build-index, an inconsistency its own skill documents as a
|
|
163
|
+
# common mistake. Both spellings are accepted here, so muscle memory from
|
|
164
|
+
# either CLI works and neither is a trap.
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@click.command("build-sqlite")
|
|
168
|
+
@click.option("--repo", default=".", show_default=True, help="Repository root directory.")
|
|
169
|
+
@click.option(
|
|
170
|
+
"--db",
|
|
171
|
+
"--sqlite",
|
|
172
|
+
"db",
|
|
173
|
+
default=None,
|
|
174
|
+
help="SQLite database path (default: <repo>/.tscodekg/graph.sqlite).",
|
|
175
|
+
)
|
|
176
|
+
@click.option(
|
|
177
|
+
"--wipe",
|
|
178
|
+
is_flag=True,
|
|
179
|
+
default=False,
|
|
180
|
+
help="Clear existing graph data before extracting.",
|
|
181
|
+
)
|
|
182
|
+
def build_sqlite(repo: str, db: str | None, wipe: bool) -> None:
|
|
183
|
+
"""Extract a TypeScript/JS knowledge graph and store it in SQLite.
|
|
184
|
+
|
|
185
|
+
The graph half of `build`; skips the vector index.
|
|
186
|
+
"""
|
|
187
|
+
_run(
|
|
188
|
+
repo=repo,
|
|
189
|
+
db=db,
|
|
190
|
+
vectors=None,
|
|
191
|
+
graph_only=True,
|
|
192
|
+
index_only=False,
|
|
193
|
+
wipe=wipe,
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@click.command("build-index")
|
|
198
|
+
@click.option("--repo", default=".", show_default=True, help="Repository root directory.")
|
|
199
|
+
@click.option(
|
|
200
|
+
"--sqlite",
|
|
201
|
+
"--db",
|
|
202
|
+
"db",
|
|
203
|
+
default=None,
|
|
204
|
+
help="Path to the existing SQLite graph (default: <repo>/.tscodekg/graph.sqlite).",
|
|
205
|
+
)
|
|
206
|
+
@click.option(
|
|
207
|
+
"--vectors",
|
|
208
|
+
default=None,
|
|
209
|
+
help="sqlite-vec store path (default: <repo>/.tscodekg/vectors.sqlite).",
|
|
210
|
+
)
|
|
211
|
+
@click.option(
|
|
212
|
+
"--wipe",
|
|
213
|
+
is_flag=True,
|
|
214
|
+
default=False,
|
|
215
|
+
help="Clear the existing vector store before indexing.",
|
|
216
|
+
)
|
|
217
|
+
def build_index(repo: str, db: str | None, vectors: str | None, wipe: bool) -> None:
|
|
218
|
+
"""Build the sqlite-vec semantic index from an existing SQLite graph.
|
|
219
|
+
|
|
220
|
+
The index half of `build`; the graph must already exist.
|
|
221
|
+
"""
|
|
222
|
+
_run(
|
|
223
|
+
repo=repo,
|
|
224
|
+
db=db,
|
|
225
|
+
vectors=vectors,
|
|
226
|
+
graph_only=False,
|
|
227
|
+
index_only=True,
|
|
228
|
+
wipe=wipe,
|
|
229
|
+
)
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""
|
|
2
|
+
cli/cmd_hooks.py — tscodekg install-hooks command.
|
|
3
|
+
|
|
4
|
+
install-hooks — install the pre-commit snapshot hook into .git/hooks/
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import stat
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
import click
|
|
13
|
+
|
|
14
|
+
# ---------------------------------------------------------------------------
|
|
15
|
+
# Hook script content (embedded so this module is self-contained when
|
|
16
|
+
# installed as a package in any repo, not just tscode_kg itself)
|
|
17
|
+
# ---------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
_PRE_COMMIT_HOOK = """\
|
|
20
|
+
#!/usr/bin/env bash
|
|
21
|
+
# TypeScriptKG pre-commit hook — runs quality checks. The index rebuild and
|
|
22
|
+
# metrics snapshot are opt-in and OFF by default; see "Why snapshots are off".
|
|
23
|
+
# Installed by: tscodekg install-hooks
|
|
24
|
+
#
|
|
25
|
+
# TSCODEKG_SNAPSHOT=1 git commit ... opt in to a per-commit snapshot
|
|
26
|
+
# TSCODEKG_SKIP_SNAPSHOT=1 git commit ... force snapshots off (wins)
|
|
27
|
+
#
|
|
28
|
+
# Note that TSCODEKG_SKIP_SNAPSHOT no longer skips the quality checks. It used
|
|
29
|
+
# to short-circuit the whole hook, so a variable named "skip snapshot" also
|
|
30
|
+
# silently skipped ruff, ty and pytest. It now gates only what it names.
|
|
31
|
+
#
|
|
32
|
+
# Why snapshots are off by default (2026-08-18)
|
|
33
|
+
# ---------------------------------------------
|
|
34
|
+
# A per-commit snapshot records `git write-tree` and is then itself staged into
|
|
35
|
+
# that same commit. Staging changes the index, so the recorded hash can never
|
|
36
|
+
# equal the tree it claims to describe — and manifest.json carries a
|
|
37
|
+
# `last_update` timestamp, so the `git add` is never a no-op. The drift is
|
|
38
|
+
# guaranteed by construction, not caused by formatting.
|
|
39
|
+
#
|
|
40
|
+
# An audit of 605 snapshots across 29 fleet manifests found 63 (10.4%) keyed to
|
|
41
|
+
# a tree any commit actually has. `snapshot diff` between adjacent entries has
|
|
42
|
+
# therefore been comparing states that never existed.
|
|
43
|
+
#
|
|
44
|
+
# The fix is to snapshot at release, keyed on the tag rather than on an
|
|
45
|
+
# ephemeral pre-commit tree. See kgrag_priv/docs/SNAPSHOT_STRATEGY.md. Until
|
|
46
|
+
# that lands, this hook runs quality checks only.
|
|
47
|
+
#
|
|
48
|
+
# This hook also used to run the rebuild and snapshot BEFORE the quality
|
|
49
|
+
# checks, which is the opposite of what it should do:
|
|
50
|
+
#
|
|
51
|
+
# * `pre-commit run` stashes unstaged changes and restores them afterwards.
|
|
52
|
+
# Building first meant the freshly-rewritten snapshots/manifest.json landed
|
|
53
|
+
# inside the stash window, where the restore could fail with "patch does not
|
|
54
|
+
# apply" and abort the commit outright — or, worse, let a staged deletion of
|
|
55
|
+
# a tracked snapshot slip into the commit.
|
|
56
|
+
# * There is no reason to pay for an index rebuild on a commit that
|
|
57
|
+
# ruff/ty/pytest is about to reject.
|
|
58
|
+
set -euo pipefail
|
|
59
|
+
|
|
60
|
+
REPO_ROOT="$(git rev-parse --show-toplevel)"
|
|
61
|
+
cd "$REPO_ROOT"
|
|
62
|
+
|
|
63
|
+
# Quality checks first (ruff, ty, pytest, detect-secrets, ...). Delegates to
|
|
64
|
+
# .pre-commit-config.yaml so quality checks stay in one place. A hook that
|
|
65
|
+
# rewrites files also exits non-zero here, so we never index a tree that is
|
|
66
|
+
# about to be reformatted.
|
|
67
|
+
PRECOMMIT="$REPO_ROOT/.venv/bin/pre-commit"
|
|
68
|
+
if [ -x "$PRECOMMIT" ]; then
|
|
69
|
+
"$PRECOMMIT" run || exit 1
|
|
70
|
+
elif command -v pre-commit &>/dev/null; then
|
|
71
|
+
pre-commit run || exit 1
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
# ---------------------------------------------------------------------------
|
|
75
|
+
# Opt-in index rebuild + snapshot. Everything below is skipped unless
|
|
76
|
+
# TSCODEKG_SNAPSHOT=1 is set, and is skipped regardless if
|
|
77
|
+
# TSCODEKG_SKIP_SNAPSHOT=1.
|
|
78
|
+
# ---------------------------------------------------------------------------
|
|
79
|
+
[ "${TSCODEKG_SNAPSHOT:-0}" = "1" ] || exit 0
|
|
80
|
+
[ "${TSCODEKG_SKIP_SNAPSHOT:-0}" = "1" ] && exit 0
|
|
81
|
+
|
|
82
|
+
# Resolve the tscodekg binary: prefer the repo's .venv, fall back to PATH.
|
|
83
|
+
if [ -x "$REPO_ROOT/.venv/bin/tscodekg" ]; then
|
|
84
|
+
TSCODEKG="$REPO_ROOT/.venv/bin/tscodekg"
|
|
85
|
+
elif command -v tscodekg &>/dev/null; then
|
|
86
|
+
TSCODEKG="tscodekg"
|
|
87
|
+
else
|
|
88
|
+
echo "[tscodekg] binary not found — skipping snapshot" >&2
|
|
89
|
+
exit 0
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
# Captured after the checks so nothing further modifies the working tree. Note
|
|
93
|
+
# the caveat above: this still cannot match the committed tree, because the
|
|
94
|
+
# `git add` below changes the index after this point.
|
|
95
|
+
TREE_HASH=$(git write-tree)
|
|
96
|
+
BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
|
97
|
+
|
|
98
|
+
# Rebuild the local index to keep it in sync with staged content.
|
|
99
|
+
"$TSCODEKG" build --repo "$REPO_ROOT" || exit 1
|
|
100
|
+
|
|
101
|
+
# Snapshot TypeScriptKG (version auto-detected from installed package).
|
|
102
|
+
"$TSCODEKG" snapshot save \\
|
|
103
|
+
--repo . \\
|
|
104
|
+
--tree-hash "$TREE_HASH" \\
|
|
105
|
+
--branch "$BRANCH" \\
|
|
106
|
+
|| { echo "[tscodekg] snapshot skipped (run 'tscodekg build' to initialize)" >&2; }
|
|
107
|
+
|
|
108
|
+
# Stage the snapshot directory so it is included in the commit. These files are
|
|
109
|
+
# added after `pre-commit run`, so they are not scanned by it — detect-secrets
|
|
110
|
+
# already excludes snapshots/ by config, which is why that is safe.
|
|
111
|
+
git add .tscodekg/snapshots/ 2>/dev/null || true
|
|
112
|
+
|
|
113
|
+
exit 0
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@click.command("install-hooks")
|
|
118
|
+
@click.option(
|
|
119
|
+
"--repo",
|
|
120
|
+
default=".",
|
|
121
|
+
type=click.Path(exists=True),
|
|
122
|
+
show_default=True,
|
|
123
|
+
help="Repository root.",
|
|
124
|
+
)
|
|
125
|
+
@click.option(
|
|
126
|
+
"--force",
|
|
127
|
+
is_flag=True,
|
|
128
|
+
help="Overwrite an existing pre-commit hook.",
|
|
129
|
+
)
|
|
130
|
+
def install_hooks(repo: str, force: bool) -> None:
|
|
131
|
+
"""Install the TypeScriptKG pre-commit git hook.
|
|
132
|
+
|
|
133
|
+
After installation, before each commit:
|
|
134
|
+
|
|
135
|
+
\b
|
|
136
|
+
1. Rebuilds the local TypeScriptKG index (full wipe)
|
|
137
|
+
2. Captures a metrics snapshot keyed by tree hash
|
|
138
|
+
3. Stages the snapshot directory atomically
|
|
139
|
+
4. Runs the pre-commit framework checks
|
|
140
|
+
|
|
141
|
+
This keeps the index in sync and ensures snapshots reflect the state of
|
|
142
|
+
the knowledge graph at commit time.
|
|
143
|
+
|
|
144
|
+
Example:
|
|
145
|
+
tscodekg install-hooks --repo .
|
|
146
|
+
"""
|
|
147
|
+
repo_root = Path(repo).resolve()
|
|
148
|
+
git_dir = repo_root / ".git"
|
|
149
|
+
|
|
150
|
+
if not git_dir.is_dir():
|
|
151
|
+
click.echo(f"Error: {repo_root} is not a git repository.", err=True)
|
|
152
|
+
raise SystemExit(1)
|
|
153
|
+
|
|
154
|
+
hooks_dir = git_dir / "hooks"
|
|
155
|
+
hooks_dir.mkdir(exist_ok=True)
|
|
156
|
+
hook_path = hooks_dir / "pre-commit"
|
|
157
|
+
|
|
158
|
+
if hook_path.exists() and not force:
|
|
159
|
+
click.echo(f"Hook already exists: {hook_path}")
|
|
160
|
+
click.echo("Use --force to overwrite.")
|
|
161
|
+
raise SystemExit(1)
|
|
162
|
+
|
|
163
|
+
hook_path.write_text(_PRE_COMMIT_HOOK)
|
|
164
|
+
mode = hook_path.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
|
|
165
|
+
hook_path.chmod(mode)
|
|
166
|
+
|
|
167
|
+
click.echo(f"OK Installed pre-commit hook: {hook_path}")
|
|
168
|
+
click.echo(" Quality checks run on every commit.")
|
|
169
|
+
click.echo(" Snapshots are OFF by default - see kgrag_priv/docs/SNAPSHOT_STRATEGY.md.")
|
|
170
|
+
click.echo(" Opt in with: TSCODEKG_SNAPSHOT=1 git commit ...")
|
|
171
|
+
click.echo(" Force off: TSCODEKG_SKIP_SNAPSHOT=1 git commit ...")
|
|
172
|
+
click.echo(" Run 'tscodekg build' first if you haven't built the graph yet.")
|
|
@@ -62,7 +62,13 @@ def _default_snapshots_dir(snapshots_dir: str | None, repo_root: Path | None = N
|
|
|
62
62
|
"--tree-hash",
|
|
63
63
|
default="",
|
|
64
64
|
type=str,
|
|
65
|
-
help="Git tree hash; auto-detected if not provided.",
|
|
65
|
+
help="Git tree hash, recorded as provenance; auto-detected if not provided.",
|
|
66
|
+
)
|
|
67
|
+
@click.option(
|
|
68
|
+
"--subject",
|
|
69
|
+
default="",
|
|
70
|
+
type=str,
|
|
71
|
+
help="What was measured, e.g. 'repo:tscode-kg'.",
|
|
66
72
|
)
|
|
67
73
|
def save_snapshot(
|
|
68
74
|
version: str | None,
|
|
@@ -71,20 +77,23 @@ def save_snapshot(
|
|
|
71
77
|
snapshots_dir: str | None,
|
|
72
78
|
branch: str | None,
|
|
73
79
|
tree_hash: str,
|
|
80
|
+
subject: str,
|
|
74
81
|
) -> None:
|
|
75
82
|
"""
|
|
76
83
|
Capture current TypeScriptKG metrics and save as a temporal snapshot.
|
|
77
84
|
|
|
78
85
|
Reads graph statistics and JSDoc coverage from the SQLite graph, runs the
|
|
79
|
-
analyzer for issue counts and hotspots, then saves a snapshot
|
|
80
|
-
|
|
81
|
-
|
|
86
|
+
analyzer for issue counts and hotspots, then saves a snapshot keyed on
|
|
87
|
+
VERSION. Omit VERSION and the snapshot is keyed on a UTC timestamp, which
|
|
88
|
+
is the right answer for a corpus with no release tag. The tree hash is
|
|
89
|
+
recorded as provenance and auto-detected from git when not provided; it
|
|
90
|
+
is not the key.
|
|
82
91
|
|
|
83
|
-
Snapshots are stored in .tscodekg/snapshots/{
|
|
92
|
+
Snapshots are stored in .tscodekg/snapshots/{key}.json, with a
|
|
84
93
|
manifest.json tracking all snapshots and their metrics.
|
|
85
94
|
|
|
86
95
|
Example:
|
|
87
|
-
tscodekg snapshot save 0.1.0 --repo .
|
|
96
|
+
tscodekg snapshot save 0.1.0 --repo . --subject repo:tscode-kg
|
|
88
97
|
"""
|
|
89
98
|
capture_snapshot(
|
|
90
99
|
version=version,
|
|
@@ -93,6 +102,7 @@ def save_snapshot(
|
|
|
93
102
|
snapshots_dir=snapshots_dir,
|
|
94
103
|
branch=branch,
|
|
95
104
|
tree_hash=tree_hash,
|
|
105
|
+
subject=subject,
|
|
96
106
|
)
|
|
97
107
|
|
|
98
108
|
|
|
@@ -104,15 +114,22 @@ def capture_snapshot(
|
|
|
104
114
|
snapshots_dir: str | None,
|
|
105
115
|
branch: str | None,
|
|
106
116
|
tree_hash: str,
|
|
117
|
+
subject: str = "",
|
|
107
118
|
) -> None:
|
|
108
119
|
"""Capture and persist a snapshot; shared by ``snapshot save`` and ``init``.
|
|
109
120
|
|
|
110
|
-
:param version:
|
|
121
|
+
:param version: Release tag, becoming the snapshot's key; auto-detected
|
|
122
|
+
from the installed package when falsy, which then yields a UTC
|
|
123
|
+
timestamp key instead -- the right answer for a corpus, not a repo
|
|
124
|
+
release. Never used as the key without being explicitly passed here.
|
|
111
125
|
:param repo: Repository root path.
|
|
112
126
|
:param db: SQLite graph path; defaults to ``<repo>/.tscodekg/graph.sqlite``.
|
|
113
127
|
:param snapshots_dir: Snapshots directory; defaults to ``<repo>/.tscodekg/snapshots``.
|
|
114
128
|
:param branch: Branch name; auto-detected when ``None``.
|
|
115
|
-
:param tree_hash: Git tree hash; auto-detected
|
|
129
|
+
:param tree_hash: Git tree hash, recorded as provenance; auto-detected
|
|
130
|
+
when empty. It is not the snapshot's key.
|
|
131
|
+
:param subject: What was measured, e.g. ``repo:tscode-kg``. Recorded
|
|
132
|
+
separately from ``version``, which names the measuring tool.
|
|
116
133
|
"""
|
|
117
134
|
from tscode_kg.kg import TypeScriptKG # pylint: disable=import-outside-toplevel
|
|
118
135
|
from tscode_kg.snapshots import SnapshotManager # pylint: disable=import-outside-toplevel
|
|
@@ -171,6 +188,10 @@ def capture_snapshot(
|
|
|
171
188
|
hotspots=hotspots,
|
|
172
189
|
issues=issue_strings,
|
|
173
190
|
tree_hash=tree_hash,
|
|
191
|
+
# An explicit VERSION is a release tag and becomes the key. An
|
|
192
|
+
# auto-detected one is the measuring tool's version and must not be.
|
|
193
|
+
key=version or "",
|
|
194
|
+
subject=subject,
|
|
174
195
|
)
|
|
175
196
|
|
|
176
197
|
snapshot_file = snap_mgr.save_snapshot(snapshot_obj)
|
|
@@ -19,7 +19,7 @@ import click
|
|
|
19
19
|
|
|
20
20
|
from tscode_kg.cli.cmd_analyze import analyze
|
|
21
21
|
from tscode_kg.cli.cmd_bridges import bridges
|
|
22
|
-
from tscode_kg.cli.cmd_build import build
|
|
22
|
+
from tscode_kg.cli.cmd_build import build, build_index, build_sqlite, update
|
|
23
23
|
from tscode_kg.cli.cmd_centrality import centrality
|
|
24
24
|
from tscode_kg.cli.cmd_explain import explain
|
|
25
25
|
from tscode_kg.cli.cmd_framework_nodes import framework_nodes
|
|
@@ -40,6 +40,9 @@ def cli() -> None:
|
|
|
40
40
|
|
|
41
41
|
cli.add_command(init)
|
|
42
42
|
cli.add_command(build)
|
|
43
|
+
cli.add_command(update)
|
|
44
|
+
cli.add_command(build_sqlite)
|
|
45
|
+
cli.add_command(build_index)
|
|
43
46
|
cli.add_command(query)
|
|
44
47
|
cli.add_command(pack)
|
|
45
48
|
cli.add_command(analyze)
|
|
@@ -8,15 +8,19 @@ those types and adds a ``SnapshotManager`` subclass that defaults
|
|
|
8
8
|
``package_name`` to ``"tscode-kg"`` so version auto-detection resolves to this
|
|
9
9
|
package.
|
|
10
10
|
|
|
11
|
-
Snapshots are
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
Snapshots are keyed on a caller-supplied release tag or, absent one, a UTC
|
|
12
|
+
timestamp -- never a git tree hash, which is read before ``git add`` stages
|
|
13
|
+
the snapshot and so names a tree that is never committed. Stored in
|
|
14
|
+
``.tscodekg/snapshots/{key}.json`` with a ``manifest.json`` tracking all
|
|
15
|
+
snapshots and their metrics — the same layout PyCodeKG uses under
|
|
16
|
+
``.pycodekg/snapshots/``.
|
|
14
17
|
|
|
15
18
|
Usage
|
|
16
19
|
-----
|
|
17
20
|
>>> from tscode_kg.snapshots import SnapshotManager
|
|
18
21
|
>>> mgr = SnapshotManager(".tscodekg/snapshots")
|
|
19
|
-
>>> snapshot = mgr.capture(version="0.
|
|
22
|
+
>>> snapshot = mgr.capture(version="0.4.0", branch="main", key="0.4.0",
|
|
23
|
+
... subject="repo:tscode-kg", graph_stats_dict=stats)
|
|
20
24
|
>>> mgr.save_snapshot(snapshot)
|
|
21
25
|
>>> manifest = mgr.load_manifest()
|
|
22
26
|
|
tscode_kg-0.2.0/LICENSE
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
BSD 2-Clause License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026, Flux-Frontiers
|
|
4
|
-
|
|
5
|
-
Redistribution and use in source and binary forms, with or without
|
|
6
|
-
modification, are permitted provided that the following conditions are met:
|
|
7
|
-
|
|
8
|
-
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
-
list of conditions and the following disclaimer.
|
|
10
|
-
|
|
11
|
-
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
-
this list of conditions and the following disclaimer in the documentation
|
|
13
|
-
and/or other materials provided with the distribution.
|
|
14
|
-
|
|
15
|
-
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
16
|
-
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
17
|
-
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
18
|
-
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
19
|
-
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
20
|
-
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
21
|
-
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
22
|
-
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
23
|
-
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
24
|
-
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
cli/cmd_build.py — tscodekg build command.
|
|
3
|
-
|
|
4
|
-
Builds the SQLite graph and sqlite-vec vector index from a TypeScript/JS repo.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
from pathlib import Path
|
|
10
|
-
|
|
11
|
-
import click
|
|
12
|
-
from rich.console import Console
|
|
13
|
-
|
|
14
|
-
console = Console()
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@click.command("build")
|
|
18
|
-
@click.option("--repo", default=".", show_default=True, help="Repository root directory.")
|
|
19
|
-
@click.option(
|
|
20
|
-
"--db",
|
|
21
|
-
default=None,
|
|
22
|
-
help="SQLite database path (default: <repo>/.tscodekg/graph.sqlite).",
|
|
23
|
-
)
|
|
24
|
-
@click.option(
|
|
25
|
-
"--vectors",
|
|
26
|
-
default=None,
|
|
27
|
-
help="sqlite-vec store path (default: <repo>/.tscodekg/vectors.sqlite).",
|
|
28
|
-
)
|
|
29
|
-
@click.option("--wipe", is_flag=True, default=False, help="Clear existing data before building.")
|
|
30
|
-
@click.option(
|
|
31
|
-
"--graph-only",
|
|
32
|
-
is_flag=True,
|
|
33
|
-
default=False,
|
|
34
|
-
help="Build SQLite graph only; skip vector index.",
|
|
35
|
-
)
|
|
36
|
-
@click.option(
|
|
37
|
-
"--index-only",
|
|
38
|
-
is_flag=True,
|
|
39
|
-
default=False,
|
|
40
|
-
help="Build vector index only; graph must already exist.",
|
|
41
|
-
)
|
|
42
|
-
def build(
|
|
43
|
-
repo: str,
|
|
44
|
-
db: str | None,
|
|
45
|
-
vectors: str | None,
|
|
46
|
-
wipe: bool,
|
|
47
|
-
graph_only: bool,
|
|
48
|
-
index_only: bool,
|
|
49
|
-
) -> None:
|
|
50
|
-
"""Build the TypeScript/JS knowledge graph for a repository."""
|
|
51
|
-
from tscode_kg.kg import TypeScriptKG # pylint: disable=import-outside-toplevel
|
|
52
|
-
|
|
53
|
-
repo_path = Path(repo).resolve()
|
|
54
|
-
if not repo_path.is_dir():
|
|
55
|
-
console.print(f"[red]Error:[/red] Repository not found: {repo_path}")
|
|
56
|
-
raise SystemExit(1)
|
|
57
|
-
|
|
58
|
-
kg = TypeScriptKG(
|
|
59
|
-
repo_root=repo_path,
|
|
60
|
-
db_path=db,
|
|
61
|
-
vectors_path=vectors,
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
console.print("[bold]TypeScriptKG build[/bold]")
|
|
65
|
-
console.print(f" repo : {repo_path}")
|
|
66
|
-
console.print(f" db : {kg.db_path}")
|
|
67
|
-
console.print(f" vectors : {kg.vectors_path}")
|
|
68
|
-
console.print(f" wipe : {wipe}")
|
|
69
|
-
console.print()
|
|
70
|
-
|
|
71
|
-
try:
|
|
72
|
-
if index_only:
|
|
73
|
-
console.print("[cyan]Building vector index...[/cyan]")
|
|
74
|
-
stats = kg.build_index(wipe=wipe)
|
|
75
|
-
elif graph_only:
|
|
76
|
-
console.print("[cyan]Building SQLite graph...[/cyan]")
|
|
77
|
-
stats = kg.build_graph(wipe=wipe)
|
|
78
|
-
else:
|
|
79
|
-
console.print("[cyan]Building graph + vector index...[/cyan]")
|
|
80
|
-
stats = kg.build(wipe=wipe)
|
|
81
|
-
|
|
82
|
-
console.print("[green]Done.[/green]")
|
|
83
|
-
console.print(str(stats))
|
|
84
|
-
except Exception as exc: # pylint: disable=broad-except
|
|
85
|
-
console.print(f"[red]Build failed:[/red] {exc}")
|
|
86
|
-
raise SystemExit(1) from exc
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
cli/cmd_hooks.py — tscodekg install-hooks command.
|
|
3
|
-
|
|
4
|
-
install-hooks — install the pre-commit snapshot hook into .git/hooks/
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
import stat
|
|
10
|
-
from pathlib import Path
|
|
11
|
-
|
|
12
|
-
import click
|
|
13
|
-
|
|
14
|
-
# ---------------------------------------------------------------------------
|
|
15
|
-
# Hook script content (embedded so this module is self-contained when
|
|
16
|
-
# installed as a package in any repo, not just tscode_kg itself)
|
|
17
|
-
# ---------------------------------------------------------------------------
|
|
18
|
-
|
|
19
|
-
_PRE_COMMIT_HOOK = """\
|
|
20
|
-
#!/usr/bin/env bash
|
|
21
|
-
# TypeScriptKG pre-commit hook — keeps the local index in sync and captures
|
|
22
|
-
# metrics snapshots BEFORE quality checks run.
|
|
23
|
-
# Installed by: tscodekg install-hooks
|
|
24
|
-
# Skip with: TSCODEKG_SKIP_SNAPSHOT=1 git commit ...
|
|
25
|
-
set -euo pipefail
|
|
26
|
-
|
|
27
|
-
[ "${TSCODEKG_SKIP_SNAPSHOT:-0}" = "1" ] && exit 0
|
|
28
|
-
|
|
29
|
-
REPO_ROOT="$(git rev-parse --show-toplevel)"
|
|
30
|
-
cd "$REPO_ROOT"
|
|
31
|
-
|
|
32
|
-
# Resolve the tscodekg binary: prefer the repo's .venv, fall back to PATH.
|
|
33
|
-
if [ -x "$REPO_ROOT/.venv/bin/tscodekg" ]; then
|
|
34
|
-
TSCODEKG="$REPO_ROOT/.venv/bin/tscodekg"
|
|
35
|
-
elif command -v tscodekg &>/dev/null; then
|
|
36
|
-
TSCODEKG="tscodekg"
|
|
37
|
-
else
|
|
38
|
-
echo "[tscodekg] binary not found — skipping snapshot hook" >&2
|
|
39
|
-
exit 0
|
|
40
|
-
fi
|
|
41
|
-
|
|
42
|
-
# Capture the tree hash of the staged index NOW — before any tool modifies files.
|
|
43
|
-
TREE_HASH=$(git write-tree)
|
|
44
|
-
BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
|
45
|
-
|
|
46
|
-
# Rebuild the local index to keep it in sync with staged content.
|
|
47
|
-
"$TSCODEKG" build --repo "$REPO_ROOT" || exit 1
|
|
48
|
-
|
|
49
|
-
# Snapshot TypeScriptKG (version auto-detected from installed package).
|
|
50
|
-
"$TSCODEKG" snapshot save \\
|
|
51
|
-
--repo . \\
|
|
52
|
-
--tree-hash "$TREE_HASH" \\
|
|
53
|
-
--branch "$BRANCH" \\
|
|
54
|
-
|| { echo "[tscodekg] snapshot skipped (run 'tscodekg build' to initialize)" >&2; }
|
|
55
|
-
|
|
56
|
-
# Stage the snapshot directory so it is included in the commit.
|
|
57
|
-
git add .tscodekg/snapshots/ 2>/dev/null || true
|
|
58
|
-
|
|
59
|
-
# Run pre-commit framework checks (ruff, ty, detect-secrets, etc.) AFTER
|
|
60
|
-
# snapshots are captured and staged. Delegates to .pre-commit-config.yaml so
|
|
61
|
-
# quality checks stay in one place.
|
|
62
|
-
PRECOMMIT="$REPO_ROOT/.venv/bin/pre-commit"
|
|
63
|
-
if [ -x "$PRECOMMIT" ]; then
|
|
64
|
-
"$PRECOMMIT" run || exit 1
|
|
65
|
-
elif command -v pre-commit &>/dev/null; then
|
|
66
|
-
pre-commit run || exit 1
|
|
67
|
-
fi
|
|
68
|
-
|
|
69
|
-
exit 0
|
|
70
|
-
"""
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
@click.command("install-hooks")
|
|
74
|
-
@click.option(
|
|
75
|
-
"--repo",
|
|
76
|
-
default=".",
|
|
77
|
-
type=click.Path(exists=True),
|
|
78
|
-
show_default=True,
|
|
79
|
-
help="Repository root.",
|
|
80
|
-
)
|
|
81
|
-
@click.option(
|
|
82
|
-
"--force",
|
|
83
|
-
is_flag=True,
|
|
84
|
-
help="Overwrite an existing pre-commit hook.",
|
|
85
|
-
)
|
|
86
|
-
def install_hooks(repo: str, force: bool) -> None:
|
|
87
|
-
"""Install the TypeScriptKG pre-commit git hook.
|
|
88
|
-
|
|
89
|
-
After installation, before each commit:
|
|
90
|
-
|
|
91
|
-
\b
|
|
92
|
-
1. Rebuilds the local TypeScriptKG index (full wipe)
|
|
93
|
-
2. Captures a metrics snapshot keyed by tree hash
|
|
94
|
-
3. Stages the snapshot directory atomically
|
|
95
|
-
4. Runs the pre-commit framework checks
|
|
96
|
-
|
|
97
|
-
This keeps the index in sync and ensures snapshots reflect the state of
|
|
98
|
-
the knowledge graph at commit time.
|
|
99
|
-
|
|
100
|
-
Example:
|
|
101
|
-
tscodekg install-hooks --repo .
|
|
102
|
-
"""
|
|
103
|
-
repo_root = Path(repo).resolve()
|
|
104
|
-
git_dir = repo_root / ".git"
|
|
105
|
-
|
|
106
|
-
if not git_dir.is_dir():
|
|
107
|
-
click.echo(f"Error: {repo_root} is not a git repository.", err=True)
|
|
108
|
-
raise SystemExit(1)
|
|
109
|
-
|
|
110
|
-
hooks_dir = git_dir / "hooks"
|
|
111
|
-
hooks_dir.mkdir(exist_ok=True)
|
|
112
|
-
hook_path = hooks_dir / "pre-commit"
|
|
113
|
-
|
|
114
|
-
if hook_path.exists() and not force:
|
|
115
|
-
click.echo(f"Hook already exists: {hook_path}")
|
|
116
|
-
click.echo("Use --force to overwrite.")
|
|
117
|
-
raise SystemExit(1)
|
|
118
|
-
|
|
119
|
-
hook_path.write_text(_PRE_COMMIT_HOOK)
|
|
120
|
-
mode = hook_path.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
|
|
121
|
-
hook_path.chmod(mode)
|
|
122
|
-
|
|
123
|
-
click.echo(f"OK Installed pre-commit hook: {hook_path}")
|
|
124
|
-
click.echo(" Snapshots will be captured automatically before each commit.")
|
|
125
|
-
click.echo(" Run 'tscodekg build' first if you haven't built the graph yet.")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|