sidegraph 0.1.0__py3-none-any.whl
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.
- sidegraph/__init__.py +37 -0
- sidegraph/anchoring.py +246 -0
- sidegraph/bootstrap/__init__.py +49 -0
- sidegraph/bootstrap/apply.py +603 -0
- sidegraph/bootstrap/catalog.py +92 -0
- sidegraph/bootstrap/cli.py +827 -0
- sidegraph/bootstrap/integrations.py +184 -0
- sidegraph/bootstrap/model.py +277 -0
- sidegraph/bootstrap/planner.py +400 -0
- sidegraph/bootstrap/proof.py +103 -0
- sidegraph/bootstrap/review.py +331 -0
- sidegraph/bootstrap/scan.py +289 -0
- sidegraph/capture.py +1794 -0
- sidegraph/cli.py +1902 -0
- sidegraph/config.py +148 -0
- sidegraph/doc_import.py +2099 -0
- sidegraph/doctor.py +1429 -0
- sidegraph/domains.py +902 -0
- sidegraph/engine/__init__.py +7 -0
- sidegraph/engine/reader.py +353 -0
- sidegraph/gitio.py +572 -0
- sidegraph/host/__init__.py +7 -0
- sidegraph/host/hooks.py +770 -0
- sidegraph/importer.py +239 -0
- sidegraph/okf.py +471 -0
- sidegraph/profiles.py +459 -0
- sidegraph/retrieval.py +1657 -0
- sidegraph/schema.py +386 -0
- sidegraph/server.py +2608 -0
- sidegraph/store.py +3363 -0
- sidegraph/sync.py +885 -0
- sidegraph/verify.py +1040 -0
- sidegraph/viz/__init__.py +4 -0
- sidegraph/viz/assets/vis-network.min.js +33 -0
- sidegraph/viz/model.py +248 -0
- sidegraph/viz/render.py +110 -0
- sidegraph/viz/template.html +131 -0
- sidegraph-0.1.0.dist-info/METADATA +392 -0
- sidegraph-0.1.0.dist-info/RECORD +42 -0
- sidegraph-0.1.0.dist-info/WHEEL +4 -0
- sidegraph-0.1.0.dist-info/entry_points.txt +18 -0
- sidegraph-0.1.0.dist-info/licenses/LICENSE +201 -0
sidegraph/config.py
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""Shared store-path resolution (design §5: Configuration & wiring).
|
|
2
|
+
|
|
3
|
+
The ONE place ``cli.py``, ``server.py``, and ``host/hooks.py`` resolve a bare
|
|
4
|
+
``Store(...)`` path from, so all three agree on env-var precedence and the
|
|
5
|
+
``SIDEGRAPH_DB`` legacy-dispatch rule. Deliberately neutral and dependency-free
|
|
6
|
+
(stdlib only) so every caller can import it cheaply: ``host/hooks.py`` needs it without
|
|
7
|
+
dragging in ``server.py``'s FastMCP app, and ``server.py`` needs it without dragging in
|
|
8
|
+
``cli.py``'s argparse machinery.
|
|
9
|
+
|
|
10
|
+
Precedence (an explicit path wins outright — nothing below it is even consulted)::
|
|
11
|
+
|
|
12
|
+
explicit arg > $SIDEGRAPH_DIR > $SIDEGRAPH_DB (deprecated) > existing ``.sidegraph/``
|
|
13
|
+
> default ``.sidegraph`` (first-creation warning)
|
|
14
|
+
|
|
15
|
+
See ``docs/reference/configuration.md`` for the full precedence and path-resolution rules.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import os
|
|
21
|
+
import sys
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
#: The canonical, directory-based store convention (design §1) — the primary default.
|
|
25
|
+
DEFAULT_STORE_DIR = ".sidegraph"
|
|
26
|
+
|
|
27
|
+
# Emitted at most once per PROCESS (module-level flag, not per-call): a long-lived server
|
|
28
|
+
# or a hook that re-resolves the path on every request must not spam stderr repeatedly.
|
|
29
|
+
# Tests reset this via ``monkeypatch.setattr(config, "_deprecation_warned", False)``.
|
|
30
|
+
_deprecation_warned = False
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _dispatch_sidegraph_db(value: str, anchor) -> str:
|
|
34
|
+
"""Apply the ``SIDEGRAPH_DB`` legacy-dispatch rule (pinned from review):
|
|
35
|
+
|
|
36
|
+
- an existing legacy ``*.db`` FILE -> pass through unchanged; ``Store`` migrates it
|
|
37
|
+
on open (see ``Store._migrate_legacy``).
|
|
38
|
+
- an existing DIRECTORY (canonical layout or not — ``Store`` already knows how to
|
|
39
|
+
open or populate either shape) -> use it directly, never its parent.
|
|
40
|
+
- a NONEXISTENT path that looks like a file sitting inside a directory (e.g.
|
|
41
|
+
``.sidegraph/decisions.db``) -> use its PARENT directory instead. This rescues
|
|
42
|
+
every old config snippet in the wild (``SIDEGRAPH_DB=.sidegraph/decisions.db``) by
|
|
43
|
+
pointing ``Store`` at the ``.sidegraph`` directory, rather than creating a fresh
|
|
44
|
+
canonical layout literally named ``decisions.db``.
|
|
45
|
+
- a NONEXISTENT BARE filename with no directory component at all (e.g. the historic
|
|
46
|
+
bare-file default ``SIDEGRAPH_DB=sidegraph.db``) -> fall through to
|
|
47
|
+
``DEFAULT_STORE_DIR`` instead. "Its parent" for a bare filename is the CURRENT
|
|
48
|
+
DIRECTORY itself (or ``$CLAUDE_PROJECT_DIR`` under a host anchor) — rescuing to
|
|
49
|
+
that would make the entire root the store: canonical layout, ``.gitignore``, and
|
|
50
|
+
the root tmp-sweep (see ``Store._sweep_stale_tmp_files``) all operating on
|
|
51
|
+
whatever else happens to live there. Checked against the RAW (pre-anchor) value:
|
|
52
|
+
anchoring a bare filename still doesn't give it a real "directory it lives in" to
|
|
53
|
+
rescue to, it just moves the ambiguity from cwd onto the anchor root instead.
|
|
54
|
+
|
|
55
|
+
``anchor`` is the same relative-path anchoring callable ``resolve_store_path`` builds
|
|
56
|
+
(identity when no ``root`` was given) — reused both to resolve ``value`` against the
|
|
57
|
+
filesystem and, in the bare-filename case, to anchor the ``DEFAULT_STORE_DIR`` fallback
|
|
58
|
+
consistently.
|
|
59
|
+
"""
|
|
60
|
+
anchored = anchor(value)
|
|
61
|
+
path = Path(anchored)
|
|
62
|
+
if path.is_file() or path.is_dir():
|
|
63
|
+
return anchored
|
|
64
|
+
if Path(value).parent == Path("."):
|
|
65
|
+
return anchor(DEFAULT_STORE_DIR)
|
|
66
|
+
return str(path.parent)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _warn_deprecated_sidegraph_db(value: str) -> None:
|
|
70
|
+
global _deprecation_warned
|
|
71
|
+
if _deprecation_warned:
|
|
72
|
+
return
|
|
73
|
+
_deprecation_warned = True
|
|
74
|
+
print(
|
|
75
|
+
f"sidegraph: $SIDEGRAPH_DB is deprecated (was {value!r}); set $SIDEGRAPH_DIR to "
|
|
76
|
+
"the store directory instead",
|
|
77
|
+
file=sys.stderr,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# The bridge between processes (D2): the PreToolUse hook knows the host's session id, the
|
|
82
|
+
# MCP server does not. SessionStart publishes it here — index-only key/value, the same
|
|
83
|
+
# mechanism host/hooks.py's `_PRETOOL_NUDGE_KEY_PREFIX` uses. The value carries a write
|
|
84
|
+
# timestamp because `meta` never expires on its own (this repo's live store holds 34 stale
|
|
85
|
+
# nudge keys), and a key that outlived its session would attribute every later CLI or pytest
|
|
86
|
+
# retrieval to it. Defined here, not in host/hooks.py, so server.py (core) can read it
|
|
87
|
+
# without importing the host seam — host/hooks.py re-exports it for its own callers/tests.
|
|
88
|
+
TELEMETRY_SESSION_KEY = "telemetry:session"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def telemetry_enabled() -> bool:
|
|
92
|
+
"""True unless ``SIDEGRAPH_TELEMETRY=off``. Opt-out, read at point of use and never
|
|
93
|
+
cached at import — one definition, two consumers (the MCP server and the PreToolUse
|
|
94
|
+
hook), because two copies of an env check drift."""
|
|
95
|
+
return os.environ.get("SIDEGRAPH_TELEMETRY", "on").strip().lower() != "off"
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def resolve_store_path(
|
|
99
|
+
explicit: str | None = None,
|
|
100
|
+
*,
|
|
101
|
+
root: str | None = None,
|
|
102
|
+
warn_on_create: bool = True,
|
|
103
|
+
) -> str:
|
|
104
|
+
"""Resolve the store path a caller should pass to ``Store(...)``.
|
|
105
|
+
|
|
106
|
+
``explicit`` is an already-parsed ``--db`` CLI flag (``None`` if the flag was
|
|
107
|
+
omitted). When given, it wins outright — no env var or default is even consulted,
|
|
108
|
+
matching every existing CLI's convention.
|
|
109
|
+
|
|
110
|
+
``root`` anchors a RELATIVE result to a base directory — plugin hooks may run with an
|
|
111
|
+
arbitrary cwd (see ``host.hooks._env_path``); ``None`` (the default) leaves relative
|
|
112
|
+
results as-is, resolved against cwd like every non-host caller.
|
|
113
|
+
|
|
114
|
+
``warn_on_create`` gates the "creating new store" stderr notice emitted when nothing
|
|
115
|
+
resolved and the default ``.sidegraph`` doesn't exist yet — ``sidegraph-init`` passes
|
|
116
|
+
``False`` since it already announces the same fact on stdout.
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
def _anchor(value: str) -> str:
|
|
120
|
+
if root and not os.path.isabs(value):
|
|
121
|
+
return os.path.join(root, value)
|
|
122
|
+
return value
|
|
123
|
+
|
|
124
|
+
if explicit is not None:
|
|
125
|
+
return _anchor(explicit)
|
|
126
|
+
|
|
127
|
+
dir_env = os.environ.get("SIDEGRAPH_DIR")
|
|
128
|
+
if dir_env:
|
|
129
|
+
return _anchor(dir_env)
|
|
130
|
+
|
|
131
|
+
db_env = os.environ.get("SIDEGRAPH_DB")
|
|
132
|
+
if db_env:
|
|
133
|
+
# SIDEGRAPH_DIR unset (or empty) is what makes SIDEGRAPH_DB "actually used" —
|
|
134
|
+
# the deprecation note fires here, never when SIDEGRAPH_DIR already won above.
|
|
135
|
+
_warn_deprecated_sidegraph_db(db_env)
|
|
136
|
+
return _dispatch_sidegraph_db(db_env, _anchor)
|
|
137
|
+
|
|
138
|
+
default_path = _anchor(DEFAULT_STORE_DIR)
|
|
139
|
+
if Path(default_path).exists():
|
|
140
|
+
return default_path
|
|
141
|
+
|
|
142
|
+
if warn_on_create:
|
|
143
|
+
print(
|
|
144
|
+
f"warning: creating new store at {default_path}; pass --db or set "
|
|
145
|
+
"SIDEGRAPH_DIR if this is not the store you meant",
|
|
146
|
+
file=sys.stderr,
|
|
147
|
+
)
|
|
148
|
+
return default_path
|