engramkeep 0.5.1__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.
- engramkeep/__init__.py +7 -0
- engramkeep/__main__.py +8 -0
- engramkeep/adapters/__init__.py +9 -0
- engramkeep/cli.py +488 -0
- engramkeep/concepts.py +198 -0
- engramkeep/config.py +79 -0
- engramkeep/dashboard.py +1158 -0
- engramkeep/errors.py +31 -0
- engramkeep/installer.py +561 -0
- engramkeep/mcp_server.py +649 -0
- engramkeep/models.py +260 -0
- engramkeep/paths.py +303 -0
- engramkeep/redact.py +76 -0
- engramkeep/search.py +160 -0
- engramkeep/store.py +600 -0
- engramkeep/troubleshoot.py +253 -0
- engramkeep-0.5.1.dist-info/METADATA +557 -0
- engramkeep-0.5.1.dist-info/RECORD +21 -0
- engramkeep-0.5.1.dist-info/WHEEL +4 -0
- engramkeep-0.5.1.dist-info/entry_points.txt +2 -0
- engramkeep-0.5.1.dist-info/licenses/LICENSE +21 -0
engramkeep/concepts.py
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"""Deterministic bilingual (EN/TR) concept expansion for keyword search.
|
|
2
|
+
|
|
3
|
+
Engramkeep stores decisions in plain Markdown. People write memory cards in
|
|
4
|
+
the language they think in: a card saved as "Capacitor Android keystore
|
|
5
|
+
sorunu" must still surface for the query "mobile" or "keystore". This module
|
|
6
|
+
maps common engineering concepts to their EN and TR spellings so a search in
|
|
7
|
+
one language finds cards written in the other.
|
|
8
|
+
|
|
9
|
+
The map is deliberately small, generic, and free of project-specific terms.
|
|
10
|
+
It is a scoring aid, not an ontology: matched expanded terms carry a lower
|
|
11
|
+
weight than the literal query tokens.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import re
|
|
17
|
+
|
|
18
|
+
SEMANTIC_CONCEPTS: dict[str, tuple[str, ...]] = {
|
|
19
|
+
"mobile": (
|
|
20
|
+
"mobil",
|
|
21
|
+
"android",
|
|
22
|
+
"ios",
|
|
23
|
+
"iphone",
|
|
24
|
+
"apk",
|
|
25
|
+
"capacitor",
|
|
26
|
+
"react-native",
|
|
27
|
+
"flutter",
|
|
28
|
+
"keystore",
|
|
29
|
+
"hybrid",
|
|
30
|
+
),
|
|
31
|
+
"database": (
|
|
32
|
+
"veritabanı",
|
|
33
|
+
"sqlite",
|
|
34
|
+
"postgres",
|
|
35
|
+
"postgresql",
|
|
36
|
+
"mysql",
|
|
37
|
+
"mariadb",
|
|
38
|
+
"mongodb",
|
|
39
|
+
"redis",
|
|
40
|
+
"prisma",
|
|
41
|
+
"sql",
|
|
42
|
+
"db",
|
|
43
|
+
"storage",
|
|
44
|
+
),
|
|
45
|
+
"frontend": (
|
|
46
|
+
"ön yüz",
|
|
47
|
+
"arayüz",
|
|
48
|
+
"react",
|
|
49
|
+
"next.js",
|
|
50
|
+
"nextjs",
|
|
51
|
+
"vue",
|
|
52
|
+
"svelte",
|
|
53
|
+
"pwa",
|
|
54
|
+
"tailwind",
|
|
55
|
+
"css",
|
|
56
|
+
"component",
|
|
57
|
+
"ui",
|
|
58
|
+
"ux",
|
|
59
|
+
),
|
|
60
|
+
"backend": (
|
|
61
|
+
"api",
|
|
62
|
+
"rest",
|
|
63
|
+
"graphql",
|
|
64
|
+
"fastapi",
|
|
65
|
+
"flask",
|
|
66
|
+
"django",
|
|
67
|
+
"express",
|
|
68
|
+
"node",
|
|
69
|
+
"microservice",
|
|
70
|
+
"endpoint",
|
|
71
|
+
"arka yüz",
|
|
72
|
+
),
|
|
73
|
+
"auth": (
|
|
74
|
+
"authentication",
|
|
75
|
+
"authorization",
|
|
76
|
+
"jwt",
|
|
77
|
+
"oauth",
|
|
78
|
+
"session",
|
|
79
|
+
"login",
|
|
80
|
+
"token",
|
|
81
|
+
"kimlik",
|
|
82
|
+
"yetki",
|
|
83
|
+
"şifre",
|
|
84
|
+
),
|
|
85
|
+
"devops": (
|
|
86
|
+
"docker",
|
|
87
|
+
"kubernetes",
|
|
88
|
+
"k8s",
|
|
89
|
+
"ci/cd",
|
|
90
|
+
"github actions",
|
|
91
|
+
"vercel",
|
|
92
|
+
"aws",
|
|
93
|
+
"cloudflare",
|
|
94
|
+
"nginx",
|
|
95
|
+
"deploy",
|
|
96
|
+
"dağıtım",
|
|
97
|
+
),
|
|
98
|
+
"automation": (
|
|
99
|
+
"otomasyon",
|
|
100
|
+
"webhook",
|
|
101
|
+
"workflow",
|
|
102
|
+
"cron",
|
|
103
|
+
"schedule",
|
|
104
|
+
"bot",
|
|
105
|
+
"scraper",
|
|
106
|
+
"pipeline",
|
|
107
|
+
),
|
|
108
|
+
"testing": (
|
|
109
|
+
"test",
|
|
110
|
+
"sınama",
|
|
111
|
+
"pytest",
|
|
112
|
+
"unittest",
|
|
113
|
+
"e2e",
|
|
114
|
+
"integration",
|
|
115
|
+
"mock",
|
|
116
|
+
"fixture",
|
|
117
|
+
),
|
|
118
|
+
"security": (
|
|
119
|
+
"güvenlik",
|
|
120
|
+
"vulnerability",
|
|
121
|
+
"xss",
|
|
122
|
+
"csrf",
|
|
123
|
+
"injection",
|
|
124
|
+
"redact",
|
|
125
|
+
"secret",
|
|
126
|
+
"encrypt",
|
|
127
|
+
"şifreleme",
|
|
128
|
+
),
|
|
129
|
+
"search": (
|
|
130
|
+
"arama",
|
|
131
|
+
"query",
|
|
132
|
+
"index",
|
|
133
|
+
"indeks",
|
|
134
|
+
"full-text",
|
|
135
|
+
"fts",
|
|
136
|
+
"rank",
|
|
137
|
+
"snippet",
|
|
138
|
+
),
|
|
139
|
+
"sync": (
|
|
140
|
+
"senkronizasyon",
|
|
141
|
+
"senkron",
|
|
142
|
+
"backup",
|
|
143
|
+
"yedek",
|
|
144
|
+
"restore",
|
|
145
|
+
"cloud",
|
|
146
|
+
"drive",
|
|
147
|
+
"sync",
|
|
148
|
+
),
|
|
149
|
+
"performance": (
|
|
150
|
+
"performans",
|
|
151
|
+
"latency",
|
|
152
|
+
"cache",
|
|
153
|
+
"önbellek",
|
|
154
|
+
"profiling",
|
|
155
|
+
"optimizasyon",
|
|
156
|
+
"memory",
|
|
157
|
+
"bellek",
|
|
158
|
+
),
|
|
159
|
+
"media": (
|
|
160
|
+
"görsel",
|
|
161
|
+
"image",
|
|
162
|
+
"video",
|
|
163
|
+
"audio",
|
|
164
|
+
"ffmpeg",
|
|
165
|
+
"logo",
|
|
166
|
+
"svg",
|
|
167
|
+
"banner",
|
|
168
|
+
"thumbnail",
|
|
169
|
+
),
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
_TOKEN_SPLIT = re.compile(r"[^\w/.@-]+", re.UNICODE)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def expand_query_terms(query: str) -> set[str]:
|
|
176
|
+
"""Return the normalized query terms plus matching EN/TR concept terms."""
|
|
177
|
+
normalized = " ".join(query.lower().split())
|
|
178
|
+
if not normalized:
|
|
179
|
+
return set()
|
|
180
|
+
|
|
181
|
+
tokens = {token for token in _TOKEN_SPLIT.split(normalized) if token}
|
|
182
|
+
expanded = set(tokens)
|
|
183
|
+
if not tokens:
|
|
184
|
+
expanded.add(normalized)
|
|
185
|
+
|
|
186
|
+
for primary, related in SEMANTIC_CONCEPTS.items():
|
|
187
|
+
family = (primary, *related)
|
|
188
|
+
if any(_term_matches(term, normalized, tokens) for term in family):
|
|
189
|
+
expanded.update(family)
|
|
190
|
+
return expanded
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def _term_matches(term: str, normalized: str, tokens: set[str]) -> bool:
|
|
194
|
+
if term in tokens:
|
|
195
|
+
return True
|
|
196
|
+
# Substring match in both directions so "mobil" finds "mobile"-family
|
|
197
|
+
# entries and multi-word terms like "github actions" still match.
|
|
198
|
+
return term in normalized or normalized in term
|
engramkeep/config.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Resolve the local Engramkeep data directory.
|
|
2
|
+
|
|
3
|
+
Precedence is intentionally simple and explicit:
|
|
4
|
+
|
|
5
|
+
1. the ``--home`` command-line value,
|
|
6
|
+
2. the ``ENGRAMKEEP_HOME`` environment variable,
|
|
7
|
+
3. a platform-specific default.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import platform
|
|
14
|
+
from collections.abc import Mapping
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
from .errors import ConfigError
|
|
18
|
+
|
|
19
|
+
APP_NAME = "engramkeep"
|
|
20
|
+
DISPLAY_NAME = "Engramkeep"
|
|
21
|
+
ENV_HOME = "ENGRAMKEEP_HOME"
|
|
22
|
+
SCHEMA_VERSION = 1
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def platform_default_home(
|
|
26
|
+
*,
|
|
27
|
+
system: str | None = None,
|
|
28
|
+
env: Mapping[str, str] | None = None,
|
|
29
|
+
user_home: Path | None = None,
|
|
30
|
+
) -> Path:
|
|
31
|
+
"""Return the platform-specific default data directory."""
|
|
32
|
+
system = system or platform.system()
|
|
33
|
+
env = os.environ if env is None else env
|
|
34
|
+
user_home = Path.home() if user_home is None else Path(user_home)
|
|
35
|
+
|
|
36
|
+
if system == "Windows":
|
|
37
|
+
base = env.get("LOCALAPPDATA")
|
|
38
|
+
root = Path(base) if base else user_home / "AppData" / "Local"
|
|
39
|
+
return (root / DISPLAY_NAME).resolve()
|
|
40
|
+
if system == "Darwin":
|
|
41
|
+
return (user_home / "Library" / "Application Support" / DISPLAY_NAME).resolve()
|
|
42
|
+
base = env.get("XDG_DATA_HOME")
|
|
43
|
+
root = Path(base) if base else user_home / ".local" / "share"
|
|
44
|
+
return (root / APP_NAME).resolve()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def resolve_home(
|
|
48
|
+
cli_home: str | os.PathLike[str] | None = None,
|
|
49
|
+
*,
|
|
50
|
+
env: Mapping[str, str] | None = None,
|
|
51
|
+
system: str | None = None,
|
|
52
|
+
user_home: Path | None = None,
|
|
53
|
+
) -> Path:
|
|
54
|
+
"""Resolve the active memory home.
|
|
55
|
+
|
|
56
|
+
An explicit command-line value wins over ``ENGRAMKEEP_HOME``, which wins
|
|
57
|
+
over the platform default. Empty values are ignored.
|
|
58
|
+
"""
|
|
59
|
+
env = os.environ if env is None else env
|
|
60
|
+
raw: str | os.PathLike[str] | None = cli_home
|
|
61
|
+
source = "command line"
|
|
62
|
+
|
|
63
|
+
if raw is None or str(raw).strip() == "":
|
|
64
|
+
raw = env.get(ENV_HOME)
|
|
65
|
+
source = ENV_HOME
|
|
66
|
+
if raw is None or str(raw).strip() == "":
|
|
67
|
+
return platform_default_home(system=system, env=env, user_home=user_home)
|
|
68
|
+
|
|
69
|
+
path = Path(raw).expanduser()
|
|
70
|
+
try:
|
|
71
|
+
resolved = path.resolve()
|
|
72
|
+
except OSError as exc:
|
|
73
|
+
raise ConfigError(f"cannot resolve {source} path: {path}") from exc
|
|
74
|
+
|
|
75
|
+
if resolved == Path(resolved.anchor):
|
|
76
|
+
raise ConfigError(f"{source} must not point at a filesystem root: {resolved}")
|
|
77
|
+
if resolved.exists() and not resolved.is_dir():
|
|
78
|
+
raise ConfigError(f"{source} is not a directory: {resolved}")
|
|
79
|
+
return resolved
|