contextl 1.2.5 → 1.2.7
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.
- package/README.md +1 -1
- package/package.json +1 -1
- package/python/query_engine.py +59 -9
package/README.md
CHANGED
|
@@ -84,7 +84,7 @@ Finds unused files and dead code by analyzing the dependency graph for files wit
|
|
|
84
84
|
|
|
85
85
|
Takes the exact dependency graph built by the intelligence engine and physically writes it to disk as a directory of interconnected Markdown files. Automatically injects file metadata, JSDoc/Docstring explanations, and uses standard `[[wikilinks]]` to map out dependencies. Open the generated folder as an Obsidian vault for a stunning 3D interactive graph of your architecture!
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
|
|
88
88
|
|
|
89
89
|
## How the ranking works
|
|
90
90
|
|
package/package.json
CHANGED
package/python/query_engine.py
CHANGED
|
@@ -60,6 +60,7 @@ class RankedFile:
|
|
|
60
60
|
)
|
|
61
61
|
|
|
62
62
|
|
|
63
|
+
|
|
63
64
|
def _tokenize(text: str) -> list[str]:
|
|
64
65
|
"""Lowercase, split on non-alphanumeric, remove stop words."""
|
|
65
66
|
tokens = re.findall(r"[a-z0-9]+", text.lower())
|
|
@@ -106,25 +107,64 @@ def _keyword_score(query_terms: list[str], file_path: str, idf_weights: dict[str
|
|
|
106
107
|
return score, matched
|
|
107
108
|
|
|
108
109
|
|
|
109
|
-
def _content_score(query_terms: list[str], file_content: str, idf_weights: dict[str, float]) -> tuple[float, list[str]]:
|
|
110
|
+
def _content_score(query_terms: list[str], file_path: str, file_content: str, idf_weights: dict[str, float]) -> tuple[float, list[str]]:
|
|
110
111
|
"""
|
|
111
112
|
Score how often query terms appear in the file's source code.
|
|
112
113
|
Uses TF-IDF weighting, capped to avoid huge files dominating.
|
|
114
|
+
Includes language-specific heuristics for Java and other OOP languages.
|
|
113
115
|
"""
|
|
114
|
-
|
|
116
|
+
is_oop = file_path.endswith(".java") or file_path.endswith(".ts") or file_path.endswith(".tsx")
|
|
117
|
+
content_tokens = set(re.findall(r"[a-z0-9]+", file_content.lower()))
|
|
118
|
+
|
|
119
|
+
# 1. CamelCase Splitting
|
|
120
|
+
if is_oop:
|
|
121
|
+
camel_tokens = []
|
|
122
|
+
for word in re.findall(r"[a-zA-Z]+", file_content):
|
|
123
|
+
if any(c.isupper() for c in word):
|
|
124
|
+
sub = re.sub(r"([A-Z])", r" \1", word).lower()
|
|
125
|
+
camel_tokens.extend(re.findall(r"[a-z0-9]+", sub))
|
|
126
|
+
content_tokens.update([t for t in camel_tokens if len(t) > 1])
|
|
127
|
+
|
|
128
|
+
# 2. Extract Class Declarations & Annotations
|
|
129
|
+
declared_classes = set()
|
|
130
|
+
if is_oop or file_path.endswith(".py"):
|
|
131
|
+
decls = re.findall(r"\b(?:class|interface|enum)\s+([a-zA-Z0-9_]+)", file_content)
|
|
132
|
+
for d in decls:
|
|
133
|
+
declared_classes.add(d.lower())
|
|
134
|
+
|
|
135
|
+
annotations = set()
|
|
136
|
+
if is_oop or file_path.endswith(".py"):
|
|
137
|
+
anns = re.findall(r"@([a-zA-Z0-9_]+)", file_content)
|
|
138
|
+
for a in anns:
|
|
139
|
+
annotations.add(a.lower())
|
|
140
|
+
|
|
115
141
|
matched = [t for t in query_terms if t in content_tokens]
|
|
116
142
|
|
|
117
143
|
if not query_terms:
|
|
118
144
|
return 0.0, []
|
|
119
145
|
|
|
120
|
-
score =
|
|
146
|
+
score = 0.0
|
|
147
|
+
for t in matched:
|
|
148
|
+
base_weight = idf_weights.get(t, 1.0)
|
|
149
|
+
multiplier = 1.0
|
|
150
|
+
|
|
151
|
+
# 3. Apply Multipliers
|
|
152
|
+
if t in declared_classes:
|
|
153
|
+
multiplier = 10.0
|
|
154
|
+
elif t in annotations:
|
|
155
|
+
multiplier = 5.0
|
|
156
|
+
|
|
157
|
+
score += (base_weight * multiplier)
|
|
158
|
+
|
|
121
159
|
max_score = sum(idf_weights.get(t, 1.0) for t in query_terms)
|
|
122
160
|
if max_score > 0:
|
|
123
|
-
|
|
161
|
+
base_normalized = min(1.0, sum(idf_weights.get(t, 1.0) for t in matched) / max_score)
|
|
162
|
+
bonus = score - sum(idf_weights.get(t, 1.0) for t in matched)
|
|
163
|
+
final_score = base_normalized + bonus
|
|
124
164
|
else:
|
|
125
|
-
|
|
165
|
+
final_score = 0.0
|
|
126
166
|
|
|
127
|
-
return
|
|
167
|
+
return final_score, matched
|
|
128
168
|
|
|
129
169
|
|
|
130
170
|
def _apply_neighbor_bonus(
|
|
@@ -179,6 +219,7 @@ def query(
|
|
|
179
219
|
return []
|
|
180
220
|
|
|
181
221
|
# --- Pre-computation: Global TF-IDF ---
|
|
222
|
+
import pathlib
|
|
182
223
|
total_files = len(repo_graph.nodes)
|
|
183
224
|
document_frequency = {term: 0 for term in query_terms}
|
|
184
225
|
|
|
@@ -186,9 +227,18 @@ def query(
|
|
|
186
227
|
for path, node in repo_graph.nodes.items():
|
|
187
228
|
abs_path = node.path if hasattr(node, "absolute_path") else str(__import__("pathlib").Path(repo_graph.root) / path)
|
|
188
229
|
try:
|
|
189
|
-
content = open(abs_path, encoding="utf-8").read()
|
|
230
|
+
content = open(abs_path, encoding="utf-8").read()
|
|
190
231
|
file_contents[path] = content
|
|
191
|
-
content_tokens = set(re.findall(r"[a-z0-9]+", content))
|
|
232
|
+
content_tokens = set(re.findall(r"[a-z0-9]+", content.lower()))
|
|
233
|
+
|
|
234
|
+
if path.endswith(".java") or path.endswith(".ts") or path.endswith(".tsx"):
|
|
235
|
+
camel_tokens = []
|
|
236
|
+
for word in re.findall(r"[a-zA-Z]+", content):
|
|
237
|
+
if any(c.isupper() for c in word):
|
|
238
|
+
sub = re.sub(r"([A-Z])", r" \1", word).lower()
|
|
239
|
+
camel_tokens.extend(re.findall(r"[a-z0-9]+", sub))
|
|
240
|
+
content_tokens.update([t for t in camel_tokens if len(t) > 1])
|
|
241
|
+
|
|
192
242
|
path_tokens = set(_path_tokens(path))
|
|
193
243
|
combined_tokens = content_tokens.union(path_tokens)
|
|
194
244
|
for term in query_terms:
|
|
@@ -209,7 +259,7 @@ def query(
|
|
|
209
259
|
|
|
210
260
|
for path, node in repo_graph.nodes.items():
|
|
211
261
|
kscore, kterms = _keyword_score(query_terms, path, idf_weights)
|
|
212
|
-
cscore, cterms = _content_score(query_terms, file_contents.get(path, ""), idf_weights)
|
|
262
|
+
cscore, cterms = _content_score(query_terms, path, file_contents.get(path, ""), idf_weights)
|
|
213
263
|
|
|
214
264
|
keyword_scores[path] = kscore
|
|
215
265
|
content_scores[path] = cscore
|