@allansantos-dev/smart-tool 0.9.38 → 0.9.39
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/CHANGELOG.md +9 -0
- package/README.md +1 -1
- package/dossier.py +5 -3
- package/freshness.py +7 -7
- package/package.json +1 -1
- package/version.py +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.39 - beta
|
|
4
|
+
|
|
5
|
+
- Two-letter technical terms (`ui`, `db`, `id`) count again in the shared `focus_terms`, used by `smart_search
|
|
6
|
+
mode=dossier` and `smart_scout`. Two-letter English and Portuguese function words (`is`, `of`, `de`, `em`, ...) are
|
|
7
|
+
dropped like the question words. Tests in `tests/test_dossier.py`.
|
|
8
|
+
- The npm registry and OSV checks of `freshness` no longer raise. `npm_registry_evidence` read a local `version` before
|
|
9
|
+
assigning it (UnboundLocalError on every call) and `npm_advisories` shadowed the `version` module with its parameter;
|
|
10
|
+
both return evidence or `None` as documented. Tests in `tests/test_npm_evidence.py`.
|
|
11
|
+
|
|
3
12
|
## 0.9.38 - beta
|
|
4
13
|
|
|
5
14
|
- One definition of a search term. `smart_search mode=dossier` now takes its terms from `dossier.focus_terms`, the
|
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ registered as the `playwright` server where this machine lets a browser be drive
|
|
|
16
16
|
An optional hook routes the agent's native Grep/Glob/Read/Bash/WebSearch/WebFetch calls to these tools when they
|
|
17
17
|
are cheaper, or just tells the agent what Smart Tool would do.
|
|
18
18
|
|
|
19
|
-
**Status:** 0.9.
|
|
19
|
+
**Status:** 0.9.39 beta. Windows only. What changed in each version: [CHANGELOG.md](CHANGELOG.md).
|
|
20
20
|
|
|
21
21
|
## How it works
|
|
22
22
|
|
package/dossier.py
CHANGED
|
@@ -16,7 +16,7 @@ TITLE_CHARS = 80
|
|
|
16
16
|
MAX_FILE_BYTES = 512 * 1024
|
|
17
17
|
BACKLOG_FILES = ("docs/BACKLOG.md", "BACKLOG.md")
|
|
18
18
|
INSTRUCTION_FILES = (("CLAUDE.md", "claude_md"), (".claude/CLAUDE.md", "claude_md"), ("AGENTS.md", "agents_md"))
|
|
19
|
-
_WORD = re.compile(r"[\w]{
|
|
19
|
+
_WORD = re.compile(r"[\w]{2,}", re.UNICODE)
|
|
20
20
|
_STOPWORDS = {
|
|
21
21
|
"the", "and", "for", "how", "what", "where", "which", "does", "with", "from", "that", "this", "are", "when", "who",
|
|
22
22
|
"why", "into", "used", "use", "uses", "its", "has", "have", "code", "project", "file", "files",
|
|
@@ -24,13 +24,15 @@ _STOPWORDS = {
|
|
|
24
24
|
"pela", "ele", "ela", "isso", "esse", "essa", "este", "esta", "fica", "ficam", "usa", "usado", "usada", "sobre",
|
|
25
25
|
"entre", "depois", "antes", "quando", "cada", "seu", "sua", "seus", "suas", "não", "sem", "mais", "código",
|
|
26
26
|
"projeto", "arquivo", "arquivos", "decide", "funciona",
|
|
27
|
+
"is", "in", "to", "of", "on", "at", "by", "an", "as", "it", "or", "be", "do", "if", "so", "we", "no", "up", "me", "my",
|
|
28
|
+
"he", "us", "am", "de", "da", "em", "na", "os", "um", "se", "ao", "ou", "eu", "te", "é", "já", "só",
|
|
27
29
|
}
|
|
28
30
|
_UNIT_START = re.compile(r"#{1,6}\s|[-*]\s")
|
|
29
31
|
|
|
30
32
|
|
|
31
33
|
def focus_terms(*texts):
|
|
32
|
-
"""Casefolded content words of the texts (
|
|
33
|
-
order: what a window is scored by."""
|
|
34
|
+
"""Casefolded content words of the texts (2+ characters, common question and function words dropped), camelCase
|
|
35
|
+
split, in order: what a window is scored by."""
|
|
34
36
|
found = []
|
|
35
37
|
for text in texts:
|
|
36
38
|
spaced = re.sub(r"([a-z])([A-Z])", r"\1 \2", text or "")
|
package/freshness.py
CHANGED
|
@@ -62,23 +62,23 @@ def npm_registry_evidence(package):
|
|
|
62
62
|
try:
|
|
63
63
|
with urllib.request.urlopen(request, timeout=7) as response:
|
|
64
64
|
data = json.load(response)
|
|
65
|
-
|
|
66
|
-
if not isinstance(
|
|
65
|
+
latest = data.get("version") if isinstance(data, dict) else None
|
|
66
|
+
if not isinstance(latest, str) or not latest or len(latest) > 80:
|
|
67
67
|
return None
|
|
68
68
|
return {"title": f"npm registry: {package}", "url": url,
|
|
69
|
-
"snippet": f"Latest version published on the npm registry: {
|
|
70
|
-
"tier": "npm_registry", "npm_package": package, "npm_version":
|
|
69
|
+
"snippet": f"Latest version published on the npm registry: {latest}",
|
|
70
|
+
"tier": "npm_registry", "npm_package": package, "npm_version": latest}
|
|
71
71
|
except Exception:
|
|
72
72
|
return None
|
|
73
73
|
|
|
74
74
|
|
|
75
|
-
def npm_advisories(package,
|
|
75
|
+
def npm_advisories(package, package_version):
|
|
76
76
|
"""IDs conhecidos pela OSV para a versão; None significa checagem indisponível."""
|
|
77
|
-
if (not package or not
|
|
77
|
+
if (not package or not package_version
|
|
78
78
|
or not re.fullmatch(r"(?:@[a-z0-9._-]+/)?[a-z0-9._-]+", package)):
|
|
79
79
|
return None
|
|
80
80
|
body = json.dumps({"package": {"name": package, "ecosystem": "npm"},
|
|
81
|
-
"version":
|
|
81
|
+
"version": package_version}).encode("utf-8")
|
|
82
82
|
request = urllib.request.Request(
|
|
83
83
|
"https://api.osv.dev/v1/query", data=body, method="POST",
|
|
84
84
|
headers={"Content-Type": "application/json", "Accept": "application/json",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@allansantos-dev/smart-tool",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.39",
|
|
4
4
|
"description": "Local MCP server that gives coding agents (Claude Code, Codex) cheaper, sharper tools than their built-in search. Windows.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Allan Santos",
|
package/version.py
CHANGED