tscode-kg 0.5.0__tar.gz → 0.6.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.5.0 → tscode_kg-0.6.0}/PKG-INFO +6 -6
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/README.md +3 -3
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/pyproject.toml +4 -40
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/__init__.py +1 -1
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/analysis.py +94 -2
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/mcp_server.py +19 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/LICENSE +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/__main__.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/app.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/bridge.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/centrality.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/cli/__init__.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/cli/cmd_analyze.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/cli/cmd_bridges.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/cli/cmd_build.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/cli/cmd_centrality.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/cli/cmd_explain.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/cli/cmd_framework_nodes.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/cli/cmd_hooks.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/cli/cmd_init.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/cli/cmd_mcp.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/cli/cmd_model.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/cli/cmd_query.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/cli/cmd_snapshot.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/cli/cmd_viz.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/cli/main.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/coderank.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/config.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/explain.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/extractor.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/framework_detector.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/kg.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/layout3d.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/snapshots.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/viz3d.py +0 -0
- {tscode_kg-0.5.0 → tscode_kg-0.6.0}/src/tscode_kg/viz3d_timeline.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tscode-kg
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.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
|
|
@@ -19,9 +19,9 @@ Provides-Extra: viz
|
|
|
19
19
|
Provides-Extra: viz3d
|
|
20
20
|
Requires-Dist: PyQt5 (>=5.15.11,<5.16) ; extra == "viz3d"
|
|
21
21
|
Requires-Dist: click (>=8.1.0,<9)
|
|
22
|
-
Requires-Dist: kgmodule-utils[semantic,sqlite-vec] (>=0.
|
|
22
|
+
Requires-Dist: kgmodule-utils[semantic,sqlite-vec] (>=0.23.0)
|
|
23
23
|
Requires-Dist: markdown (>=3.10,<3.11) ; extra == "viz3d"
|
|
24
|
-
Requires-Dist: mcp (>=1.
|
|
24
|
+
Requires-Dist: mcp (>=1.3.0,<2)
|
|
25
25
|
Requires-Dist: networkx (>=3.0)
|
|
26
26
|
Requires-Dist: param (>=2.4,<2.5) ; extra == "viz3d"
|
|
27
27
|
Requires-Dist: plotly (>=6.8.0,<6.9) ; extra == "viz"
|
|
@@ -42,7 +42,7 @@ Description-Content-Type: text/markdown
|
|
|
42
42
|
[](https://www.python.org/)
|
|
43
43
|
[](https://www.elastic.co/licensing/elastic-license)
|
|
44
44
|
[](https://pypi.org/project/tscode-kg/)
|
|
45
|
-
[](https://github.com/Flux-Frontiers/tscode_kg/releases)
|
|
46
46
|
[](https://github.com/Flux-Frontiers/tscode_kg/actions/workflows/ci.yml)
|
|
47
47
|
[](https://python-poetry.org/)
|
|
48
48
|
[](https://doi.org/10.5281/zenodo.22559450)
|
|
@@ -195,13 +195,13 @@ If you use TypeScriptKG in your research or project, please cite it:
|
|
|
195
195
|
|
|
196
196
|
[](https://doi.org/10.5281/zenodo.22559450)
|
|
197
197
|
|
|
198
|
-
> Suchanek, E. G. (2026). *TypeScriptKG: Semantic Knowledge Graph for TypeScript/JavaScript Codebases* (Version 0.
|
|
198
|
+
> Suchanek, E. G. (2026). *TypeScriptKG: Semantic Knowledge Graph for TypeScript/JavaScript Codebases* (Version 0.6.0) [Software]. Flux-Frontiers. https://doi.org/10.5281/zenodo.22559450
|
|
199
199
|
|
|
200
200
|
```bibtex
|
|
201
201
|
@software{suchanek_tscode_kg,
|
|
202
202
|
author = {Suchanek, Eric G.},
|
|
203
203
|
title = {{TypeScriptKG}: Semantic Knowledge Graph for TypeScript/JavaScript Codebases},
|
|
204
|
-
version = {0.
|
|
204
|
+
version = {0.6.0},
|
|
205
205
|
year = {2026},
|
|
206
206
|
publisher = {Flux-Frontiers},
|
|
207
207
|
doi = {10.5281/zenodo.22559450},
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://www.python.org/)
|
|
4
4
|
[](https://www.elastic.co/licensing/elastic-license)
|
|
5
5
|
[](https://pypi.org/project/tscode-kg/)
|
|
6
|
-
[](https://github.com/Flux-Frontiers/tscode_kg/releases)
|
|
7
7
|
[](https://github.com/Flux-Frontiers/tscode_kg/actions/workflows/ci.yml)
|
|
8
8
|
[](https://python-poetry.org/)
|
|
9
9
|
[](https://doi.org/10.5281/zenodo.22559450)
|
|
@@ -156,13 +156,13 @@ If you use TypeScriptKG in your research or project, please cite it:
|
|
|
156
156
|
|
|
157
157
|
[](https://doi.org/10.5281/zenodo.22559450)
|
|
158
158
|
|
|
159
|
-
> Suchanek, E. G. (2026). *TypeScriptKG: Semantic Knowledge Graph for TypeScript/JavaScript Codebases* (Version 0.
|
|
159
|
+
> Suchanek, E. G. (2026). *TypeScriptKG: Semantic Knowledge Graph for TypeScript/JavaScript Codebases* (Version 0.6.0) [Software]. Flux-Frontiers. https://doi.org/10.5281/zenodo.22559450
|
|
160
160
|
|
|
161
161
|
```bibtex
|
|
162
162
|
@software{suchanek_tscode_kg,
|
|
163
163
|
author = {Suchanek, Eric G.},
|
|
164
164
|
title = {{TypeScriptKG}: Semantic Knowledge Graph for TypeScript/JavaScript Codebases},
|
|
165
|
-
version = {0.
|
|
165
|
+
version = {0.6.0},
|
|
166
166
|
year = {2026},
|
|
167
167
|
publisher = {Flux-Frontiers},
|
|
168
168
|
doi = {10.5281/zenodo.22559450},
|
|
@@ -38,7 +38,7 @@ packages = [{ include = "tscode_kg", from = "src" }]
|
|
|
38
38
|
|
|
39
39
|
[project]
|
|
40
40
|
name = "tscode-kg"
|
|
41
|
-
version = "0.
|
|
41
|
+
version = "0.6.0"
|
|
42
42
|
description = "Knowledge graph for TypeScript/JavaScript codebases — AST extraction, hybrid semantic+structural search"
|
|
43
43
|
readme = "README.md"
|
|
44
44
|
license = "Elastic-2.0"
|
|
@@ -68,7 +68,7 @@ dependencies = [
|
|
|
68
68
|
# manager is now the package_name class attribute, where it used to be an
|
|
69
69
|
# __init__ that only forwarded to super(). Against 0.19.x the manager
|
|
70
70
|
# reports itself as "kg-utils" in every snapshot's tool field.
|
|
71
|
-
"kgmodule-utils[semantic,sqlite-vec]>=0.
|
|
71
|
+
"kgmodule-utils[semantic,sqlite-vec]>=0.23.0",
|
|
72
72
|
"click>=8.1.0,<9",
|
|
73
73
|
# Upper-bounded: mcp 2.0 removed the bundled `mcp.server.fastmcp` module
|
|
74
74
|
# (FastMCP was split out into the standalone `fastmcp` package) and rebuilt
|
|
@@ -76,7 +76,7 @@ dependencies = [
|
|
|
76
76
|
# FastMCP from mcp, so an unbounded floor lets a clean install pull 2.x and
|
|
77
77
|
# break `tscodekg-mcp` at import time. Lift this only alongside a port to
|
|
78
78
|
# the standalone fastmcp package.
|
|
79
|
-
"mcp>=1.
|
|
79
|
+
"mcp>=1.3.0,<2",
|
|
80
80
|
"networkx>=3.0",
|
|
81
81
|
]
|
|
82
82
|
|
|
@@ -123,43 +123,7 @@ pytest-cov = ">=5.0.0"
|
|
|
123
123
|
# Capped below 0.16 to match the fleet: 0.16 formats Python inside Markdown
|
|
124
124
|
# code blocks as stable behaviour. The floor was unbounded, so the lock could
|
|
125
125
|
# drift away from the version .pre-commit-config.yaml pins.
|
|
126
|
-
ruff = ">=0.
|
|
127
|
-
|
|
128
|
-
# ---------------------------------------------------------------------------
|
|
129
|
-
# Maintainer tooling (Poetry group, not a PEP 621 extra)
|
|
130
|
-
# ---------------------------------------------------------------------------
|
|
131
|
-
# pycode-kg is a tool this repo *runs*, not a library it imports: PyCodeKG
|
|
132
|
-
# indexes this repo's Python source from the outside, via the `pycodekg` CLI in
|
|
133
|
-
# .git/hooks/pre-commit. It belongs in a Poetry group -- locked and installable,
|
|
134
|
-
# but never written into the wheel, so no published extra acquires a sibling.
|
|
135
|
-
#
|
|
136
|
-
# doc-kg rides along for fleet uniformity: every repo's `kg` group carries the
|
|
137
|
-
# same two CLIs, so `poetry install --with kg` means one thing everywhere. This
|
|
138
|
-
# repo has no DocKG index today (no .mcp.json, no `dockg` invocation), so the
|
|
139
|
-
# CLI is simply available rather than required -- and it is here, not in an
|
|
140
|
-
# extra, precisely because nothing depends on it at runtime.
|
|
141
|
-
#
|
|
142
|
-
# poetry install --with kg # get the pycodekg CLI into .venv/bin
|
|
143
|
-
# poetry install # default: group is optional, skipped
|
|
144
|
-
#
|
|
145
|
-
# This supersedes the standing "install it by hand" workaround. That note
|
|
146
|
-
# described a real deadlock -- older pycode-kg capped transformers below 4.57
|
|
147
|
-
# while kgmodule-utils >=0.9.0 needs >=5.5.0, which silently held this repo on
|
|
148
|
-
# the pre-CVE transformers line. pycode-kg 0.21.4 no longer pins transformers at
|
|
149
|
-
# all; it inherits the same kgmodule-utils[semantic] constraint this project
|
|
150
|
-
# already uses, so the two no longer conflict. Verified: locking with this group
|
|
151
|
-
# keeps transformers at 5.14.1.
|
|
152
|
-
[tool.poetry.group.kg]
|
|
153
|
-
optional = true
|
|
154
|
-
|
|
155
|
-
[tool.poetry.group.kg.dependencies]
|
|
156
|
-
# Both floors are the releases that retired these packages' own snapshot
|
|
157
|
-
# overrides and moved them onto kgmodule-utils>=0.20.0, the same SDK this repo
|
|
158
|
-
# now requires. Pinned lower, `poetry install --with kg` could resolve a dockg
|
|
159
|
-
# or pycodekg predating the shared extension points into the same environment
|
|
160
|
-
# as a tscode_kg that depends on them.
|
|
161
|
-
doc-kg = ">=0.26.0"
|
|
162
|
-
pycode-kg = ">=0.27.0"
|
|
126
|
+
ruff = ">=0.15,<0.16"
|
|
163
127
|
|
|
164
128
|
[project.urls]
|
|
165
129
|
Homepage = "https://github.com/Flux-Frontiers/tscode_kg"
|
|
@@ -166,17 +166,60 @@ class TSCodeKGAnalyzer:
|
|
|
166
166
|
self.coderank_top_nodes: list[dict] = []
|
|
167
167
|
self.concern_analysis: list[dict] = []
|
|
168
168
|
self._phase_result: str = ""
|
|
169
|
+
#: Phases that could not run, with the reason. Rendered in the report
|
|
170
|
+
#: so a degraded run says what is missing rather than omitting it.
|
|
171
|
+
self.phase_failures: list[dict] = []
|
|
169
172
|
|
|
170
173
|
# ------------------------------------------------------------------
|
|
171
174
|
# Phase runner
|
|
172
175
|
# ------------------------------------------------------------------
|
|
173
176
|
|
|
177
|
+
def _take_phase_result(self) -> str:
|
|
178
|
+
"""Read and clear the one-line summary the phase just set.
|
|
179
|
+
|
|
180
|
+
Phases report through ``self._phase_result`` rather than a return
|
|
181
|
+
value, because they are called as bare ``Callable[[], None]``. Reading
|
|
182
|
+
it here rather than inline in :meth:`_run_phase` also keeps the read
|
|
183
|
+
away from that method's own ``= ""`` assignment, which a type checker
|
|
184
|
+
otherwise narrows to a literal it can never see ``fn()`` change.
|
|
185
|
+
"""
|
|
186
|
+
result = self._phase_result
|
|
187
|
+
self._phase_result = ""
|
|
188
|
+
return f" {result}" if result else ""
|
|
189
|
+
|
|
174
190
|
def _run_phase(self, num: int, name: str, fn: Callable[[], None]) -> None:
|
|
191
|
+
"""Run one analysis phase, reporting rather than propagating a failure.
|
|
192
|
+
|
|
193
|
+
A phase that cannot run must not take the other thirteen and the report
|
|
194
|
+
with it. The individual phases already guard the errors they expect
|
|
195
|
+
(``AttributeError``, ``ValueError``, ``RuntimeError``), but the ones
|
|
196
|
+
worth surviving are the ones nobody predicted -- the concrete case
|
|
197
|
+
being ``sqlite3.OperationalError: no such table: vec_nodes`` after
|
|
198
|
+
``tscodekg build-sqlite``, a supported command that deliberately builds
|
|
199
|
+
the graph without the vector index. Only phase 4 seeds on a semantic
|
|
200
|
+
query; the other thirteen are pure SQL and have everything they need.
|
|
201
|
+
Aborting the run gave the user no report at all, and an error naming
|
|
202
|
+
an internal table rather than the missing step.
|
|
203
|
+
|
|
204
|
+
The failure is recorded in :attr:`phase_failures` and rendered in the
|
|
205
|
+
report, so a degraded run says which sections are missing and why
|
|
206
|
+
instead of quietly omitting them.
|
|
207
|
+
"""
|
|
175
208
|
self._phase_result = ""
|
|
176
209
|
t0 = time.monotonic()
|
|
177
|
-
|
|
210
|
+
try:
|
|
211
|
+
fn()
|
|
212
|
+
except Exception as exc: # noqa: BLE001 -- see docstring
|
|
213
|
+
elapsed = time.monotonic() - t0
|
|
214
|
+
self.phase_failures.append({"phase": num, "name": name, "error": str(exc)})
|
|
215
|
+
logger.warning("Phase %d (%s) failed: %s", num, name, exc)
|
|
216
|
+
self.console.print(
|
|
217
|
+
f" [cyan]▶ Phase {num:2d}/{self._TOTAL_PHASES}:[/cyan]"
|
|
218
|
+
f" {name} [yellow]skipped: {exc}[/yellow] [green]({elapsed:.1f}s)[/green]"
|
|
219
|
+
)
|
|
220
|
+
return
|
|
178
221
|
elapsed = time.monotonic() - t0
|
|
179
|
-
result =
|
|
222
|
+
result = self._take_phase_result()
|
|
180
223
|
self.console.print(
|
|
181
224
|
f" [cyan]▶ Phase {num:2d}/{self._TOTAL_PHASES}:[/cyan]"
|
|
182
225
|
f" {name}{result} [green]({elapsed:.1f}s)[/green]"
|
|
@@ -1404,6 +1447,10 @@ Most-called functions and methods — potential bottlenecks or core APIs.
|
|
|
1404
1447
|
"""
|
|
1405
1448
|
)
|
|
1406
1449
|
|
|
1450
|
+
degraded = self._render_phase_failures()
|
|
1451
|
+
if degraded:
|
|
1452
|
+
report += "\n---\n\n" + degraded + "\n---\n"
|
|
1453
|
+
|
|
1407
1454
|
for i, metrics in enumerate(
|
|
1408
1455
|
sorted(self.function_metrics.values(), key=lambda m: m.fan_in, reverse=True)[:15], 1
|
|
1409
1456
|
):
|
|
@@ -1641,6 +1688,42 @@ Cohesion = incoming-callers / (incoming + outgoing + 1). Higher = more internall
|
|
|
1641
1688
|
Path(report_path).write_text(report, encoding="utf-8")
|
|
1642
1689
|
self.console.print(f"[green]✓[/green] Report written to {report_path}")
|
|
1643
1690
|
|
|
1691
|
+
def _render_phase_failures(self) -> str:
|
|
1692
|
+
"""Render the degraded-run notice, or an empty string when all phases ran.
|
|
1693
|
+
|
|
1694
|
+
A phase that could not run leaves its section of the report empty. Said
|
|
1695
|
+
plainly, that is a missing build step; left unsaid, it looks like a
|
|
1696
|
+
finding about the codebase.
|
|
1697
|
+
"""
|
|
1698
|
+
if not self.phase_failures:
|
|
1699
|
+
return ""
|
|
1700
|
+
|
|
1701
|
+
lines = ["## Incomplete Analysis\n"]
|
|
1702
|
+
lines.append(
|
|
1703
|
+
f"{len(self.phase_failures)} of {self._TOTAL_PHASES} phases could not run. "
|
|
1704
|
+
"The sections they produce are missing from this report — that is a gap in "
|
|
1705
|
+
"the analysis, not a finding about the code.\n"
|
|
1706
|
+
)
|
|
1707
|
+
lines.append("| Phase | Name | Reason |")
|
|
1708
|
+
lines.append("|---|---|---|")
|
|
1709
|
+
for failure in self.phase_failures:
|
|
1710
|
+
lines.append(f"| {failure['phase']} | {failure['name']} | `{failure['error']}` |")
|
|
1711
|
+
|
|
1712
|
+
if any(
|
|
1713
|
+
"vec_nodes" in f["error"]
|
|
1714
|
+
or "no such table" in f["error"]
|
|
1715
|
+
or "Vector store not found" in f["error"]
|
|
1716
|
+
for f in self.phase_failures
|
|
1717
|
+
):
|
|
1718
|
+
lines.append(
|
|
1719
|
+
"\n> The semantic index is missing. Only the fan-out phase needs it; "
|
|
1720
|
+
"every other phase reads the SQLite graph directly, which is why the "
|
|
1721
|
+
"rest of this report is complete. Run `tscodekg build --repo <path>` "
|
|
1722
|
+
"(or `tscodekg build-index`) to build it, then re-run the analysis."
|
|
1723
|
+
)
|
|
1724
|
+
lines.append("")
|
|
1725
|
+
return "\n".join(lines)
|
|
1726
|
+
|
|
1644
1727
|
def _compile_results(self) -> dict:
|
|
1645
1728
|
"""Compile all phase results into a serialisable dictionary."""
|
|
1646
1729
|
sorted_fn = sorted(self.function_metrics.items(), key=lambda kv: kv[1].fan_in, reverse=True)
|
|
@@ -1676,6 +1759,10 @@ Cohesion = incoming-callers / (incoming + outgoing + 1). Higher = more internall
|
|
|
1676
1759
|
],
|
|
1677
1760
|
"centrality_modules": self.centrality_modules,
|
|
1678
1761
|
"coderank_top_nodes": self.coderank_top_nodes,
|
|
1762
|
+
# A caller reading the dict rather than the Markdown needs the same
|
|
1763
|
+
# signal the report carries: an empty section here means a phase did
|
|
1764
|
+
# not run, not that the codebase has nothing to show.
|
|
1765
|
+
"phase_failures": self.phase_failures,
|
|
1679
1766
|
}
|
|
1680
1767
|
|
|
1681
1768
|
def to_markdown(self) -> str:
|
|
@@ -1693,6 +1780,11 @@ Cohesion = incoming-callers / (incoming + outgoing + 1). Higher = more internall
|
|
|
1693
1780
|
out.append(f"**Generated:** {datetime.datetime.now(datetime.UTC).isoformat()} \n")
|
|
1694
1781
|
out.append("\n---\n")
|
|
1695
1782
|
|
|
1783
|
+
degraded = self._render_phase_failures()
|
|
1784
|
+
if degraded:
|
|
1785
|
+
out.append(degraded)
|
|
1786
|
+
out.append("\n---\n")
|
|
1787
|
+
|
|
1696
1788
|
out.append("## Baseline Metrics\n")
|
|
1697
1789
|
out.append("| Metric | Value |")
|
|
1698
1790
|
out.append("|---|---|")
|
|
@@ -74,6 +74,8 @@ from __future__ import annotations
|
|
|
74
74
|
import argparse
|
|
75
75
|
import json
|
|
76
76
|
import sys
|
|
77
|
+
from collections.abc import AsyncIterator
|
|
78
|
+
from contextlib import asynccontextmanager
|
|
77
79
|
from pathlib import Path
|
|
78
80
|
|
|
79
81
|
from kg_utils.semantic import DEFAULT_MODEL
|
|
@@ -142,6 +144,22 @@ def _snapshot_freshness(snapshot_total_nodes: int) -> dict:
|
|
|
142
144
|
# MCP server
|
|
143
145
|
# ---------------------------------------------------------------------------
|
|
144
146
|
|
|
147
|
+
|
|
148
|
+
@asynccontextmanager
|
|
149
|
+
async def _lifespan(_server: FastMCP) -> AsyncIterator[None]:
|
|
150
|
+
"""Close the graph's SQLite connection(s) when the server shuts down.
|
|
151
|
+
|
|
152
|
+
``main()`` sets the module-level ``_kg`` before ``mcp.run()`` calls into
|
|
153
|
+
this, and both the stdio and SSE transports route through the same
|
|
154
|
+
underlying ``Server.run()``, so this fires on either one.
|
|
155
|
+
"""
|
|
156
|
+
try:
|
|
157
|
+
yield
|
|
158
|
+
finally:
|
|
159
|
+
if _kg is not None:
|
|
160
|
+
_kg.close()
|
|
161
|
+
|
|
162
|
+
|
|
145
163
|
mcp = FastMCP(
|
|
146
164
|
"tscodekg",
|
|
147
165
|
instructions=(
|
|
@@ -202,6 +220,7 @@ mcp = FastMCP(
|
|
|
202
220
|
"- **Track codebase evolution**: snapshot_list → snapshot_diff(key_a, key_b)\n"
|
|
203
221
|
"- **Answer 'how does X work?'**: pack_snippets with a descriptive query\n"
|
|
204
222
|
),
|
|
223
|
+
lifespan=_lifespan,
|
|
205
224
|
)
|
|
206
225
|
|
|
207
226
|
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|