contextl 1.2.42 → 1.2.45
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/package.json +1 -1
- package/python/installer.py +22 -5
- package/python/query_engine.py +38 -14
package/package.json
CHANGED
package/python/installer.py
CHANGED
|
@@ -2,9 +2,11 @@ import os
|
|
|
2
2
|
import json
|
|
3
3
|
import platform
|
|
4
4
|
import sys
|
|
5
|
+
import tempfile
|
|
6
|
+
import shutil
|
|
5
7
|
from pathlib import Path
|
|
6
8
|
|
|
7
|
-
def install_mcp():
|
|
9
|
+
def install_mcp(include_vscode_workspace: bool = False):
|
|
8
10
|
home = Path.home()
|
|
9
11
|
system = platform.system()
|
|
10
12
|
|
|
@@ -42,6 +44,16 @@ def install_mcp():
|
|
|
42
44
|
# 4. Cursor
|
|
43
45
|
targets.append(home / ".cursor" / "mcp.json")
|
|
44
46
|
|
|
47
|
+
# 5. Claude Code
|
|
48
|
+
targets.append(home / ".claude.json")
|
|
49
|
+
|
|
50
|
+
# 6. Windsurf
|
|
51
|
+
targets.append(home / ".codeium" / "windsurf" / "mcp_config.json")
|
|
52
|
+
|
|
53
|
+
# 7. VS Code (Workspace level)
|
|
54
|
+
if include_vscode_workspace:
|
|
55
|
+
targets.append(Path.cwd() / ".vscode" / "mcp.json")
|
|
56
|
+
|
|
45
57
|
success_count = 0
|
|
46
58
|
|
|
47
59
|
for path in targets:
|
|
@@ -71,14 +83,19 @@ def install_mcp():
|
|
|
71
83
|
"args": []
|
|
72
84
|
}
|
|
73
85
|
|
|
74
|
-
#
|
|
75
|
-
|
|
86
|
+
# Backup existing config
|
|
87
|
+
shutil.copy(path, path.with_suffix(path.suffix + ".bak"))
|
|
88
|
+
|
|
89
|
+
# Write config atomically
|
|
90
|
+
fd, tmp_path = tempfile.mkstemp(dir=path.parent, suffix=".tmp")
|
|
91
|
+
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
|
76
92
|
json.dump(config, f, indent=2)
|
|
93
|
+
os.replace(tmp_path, path)
|
|
77
94
|
|
|
78
|
-
print(f"
|
|
95
|
+
print(f"[SUCCESS] Successfully injected ContextL MCP Server into: {path}")
|
|
79
96
|
success_count += 1
|
|
80
97
|
except Exception as e:
|
|
81
|
-
print(f"
|
|
98
|
+
print(f"[ERROR] Failed to parse or write to {path}: {e}")
|
|
82
99
|
|
|
83
100
|
if success_count > 0:
|
|
84
101
|
print("\nInstallation successful! Please restart your IDE or AI Client (e.g. reload the window) for the changes to take effect.")
|
package/python/query_engine.py
CHANGED
|
@@ -82,6 +82,41 @@ def _path_tokens(path: str) -> list[str]:
|
|
|
82
82
|
tokens.extend(re.findall(r"[a-z0-9]+", sub))
|
|
83
83
|
return [t for t in tokens if t not in STOP_WORDS and len(t) > 1]
|
|
84
84
|
|
|
85
|
+
COMMON_ABBREVS = {
|
|
86
|
+
"db": "database",
|
|
87
|
+
"auth": "authentication",
|
|
88
|
+
"config": "configuration",
|
|
89
|
+
"pkg": "package",
|
|
90
|
+
"app": "application",
|
|
91
|
+
"svc": "service",
|
|
92
|
+
"repo": "repository",
|
|
93
|
+
"mgr": "manager",
|
|
94
|
+
"impl": "implementation",
|
|
95
|
+
"dir": "directory",
|
|
96
|
+
"req": "request",
|
|
97
|
+
"res": "response",
|
|
98
|
+
"env": "environment",
|
|
99
|
+
"src": "source",
|
|
100
|
+
"lib": "library",
|
|
101
|
+
"bin": "binary",
|
|
102
|
+
"init": "initialize",
|
|
103
|
+
"util": "utility",
|
|
104
|
+
"utils": "utilities",
|
|
105
|
+
"msg": "message",
|
|
106
|
+
"err": "error",
|
|
107
|
+
"ctx": "context",
|
|
108
|
+
"fn": "function",
|
|
109
|
+
"cmd": "command",
|
|
110
|
+
"ctrl": "controller",
|
|
111
|
+
"mid": "middleware"
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
def _is_match(term: str, target: str) -> bool:
|
|
115
|
+
term_canon = COMMON_ABBREVS.get(term, term)
|
|
116
|
+
if term_canon in target or target in term_canon or target.startswith(term_canon) or term_canon.startswith(target):
|
|
117
|
+
return True
|
|
118
|
+
return False
|
|
119
|
+
|
|
85
120
|
|
|
86
121
|
def _keyword_score(query_terms: list[str], file_path: str, idf_weights: dict[str, float]) -> tuple[float, list[str]]:
|
|
87
122
|
"""
|
|
@@ -100,10 +135,7 @@ def _keyword_score(query_terms: list[str], file_path: str, idf_weights: dict[str
|
|
|
100
135
|
score += 4.0 * idf_weights.get(term, 1.0) # Massive signal
|
|
101
136
|
matched.append(term)
|
|
102
137
|
# Substring or abbreviation match on filename
|
|
103
|
-
elif len(term) >= 2 and any(
|
|
104
|
-
term in ft or ft in term or ft.startswith(term) or term.startswith(ft)
|
|
105
|
-
for ft in filename_toks
|
|
106
|
-
):
|
|
138
|
+
elif len(term) >= 2 and any(_is_match(term, ft) for ft in filename_toks):
|
|
107
139
|
score += 3.0 * idf_weights.get(term, 1.0)
|
|
108
140
|
matched.append(term)
|
|
109
141
|
# Exact match on path
|
|
@@ -112,10 +144,7 @@ def _keyword_score(query_terms: list[str], file_path: str, idf_weights: dict[str
|
|
|
112
144
|
if term not in matched:
|
|
113
145
|
matched.append(term)
|
|
114
146
|
# Substring or abbreviation match on path
|
|
115
|
-
elif len(term) >= 2 and any(
|
|
116
|
-
term in pt or pt in term or pt.startswith(term) or term.startswith(pt)
|
|
117
|
-
for pt in path_toks
|
|
118
|
-
):
|
|
147
|
+
elif len(term) >= 2 and any(_is_match(term, pt) for pt in path_toks):
|
|
119
148
|
score += 1.5 * idf_weights.get(term, 1.0)
|
|
120
149
|
if term not in matched:
|
|
121
150
|
matched.append(term)
|
|
@@ -330,13 +359,8 @@ def query(
|
|
|
330
359
|
path_tokens = set(_path_tokens(path))
|
|
331
360
|
combined_tokens = content_tokens.union(path_tokens)
|
|
332
361
|
for term in query_terms:
|
|
333
|
-
if term in combined_tokens:
|
|
362
|
+
if any(_is_match(term, ct) for ct in combined_tokens):
|
|
334
363
|
document_frequency[term] += 1
|
|
335
|
-
elif len(term) >= 2:
|
|
336
|
-
for ct in combined_tokens:
|
|
337
|
-
if term in ct or (len(ct) >= 2 and ct in term):
|
|
338
|
-
document_frequency[term] += 1
|
|
339
|
-
break
|
|
340
364
|
|
|
341
365
|
avgdl = total_tokens_count / total_files if total_files > 0 else 1.0
|
|
342
366
|
|