adl-toolkit 0.0.0-stage → 0.1.3
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/INSTALL_TOOLKIT.md +163 -0
- package/README.md +269 -2
- package/adl_drift/__init__.py +6 -0
- package/adl_drift/cli/__init__.py +1 -0
- package/adl_drift/cli/commands.py +993 -0
- package/adl_drift/cli/main.py +744 -0
- package/adl_drift/cortex/__init__.py +1 -0
- package/adl_drift/cortex/embeddings.py +96 -0
- package/adl_drift/cortex/memory.py +1749 -0
- package/adl_drift/cortex/patterns.py +89 -0
- package/adl_drift/cortex/telemetry.py +192 -0
- package/adl_drift/detectors/__init__.py +1 -0
- package/adl_drift/detectors/async_patterns.py +115 -0
- package/adl_drift/detectors/base.py +121 -0
- package/adl_drift/detectors/config.py +122 -0
- package/adl_drift/detectors/data_access.py +128 -0
- package/adl_drift/detectors/decorators.py +122 -0
- package/adl_drift/detectors/dynamic_patterns.py +260 -0
- package/adl_drift/detectors/errors.py +134 -0
- package/adl_drift/detectors/imports.py +132 -0
- package/adl_drift/detectors/logging_patterns.py +108 -0
- package/adl_drift/detectors/routes.py +257 -0
- package/adl_drift/detectors/security.py +236 -0
- package/adl_drift/detectors/taint.py +406 -0
- package/adl_drift/detectors/tests.py +95 -0
- package/adl_drift/detectors/type_conventions.py +81 -0
- package/adl_drift/graph/__init__.py +1 -0
- package/adl_drift/graph/builder.py +2670 -0
- package/adl_drift/graph/cfg.py +461 -0
- package/adl_drift/graph/cluster.py +40 -0
- package/adl_drift/graph/edges.py +86 -0
- package/adl_drift/graph/node.py +90 -0
- package/adl_drift/harness/__init__.py +10 -0
- package/adl_drift/harness/setup.py +832 -0
- package/adl_drift/harness/skills/adl-drift.md +25 -0
- package/adl_drift/harness/skills/drift-antigravity.md +19 -0
- package/adl_drift/harness/skills/drift-generic.md +19 -0
- package/adl_drift/harness/watch.py +84 -0
- package/adl_drift/lang/__init__.py +8 -0
- package/adl_drift/lang/base.py +464 -0
- package/adl_drift/lang/java/__init__.py +6 -0
- package/adl_drift/lang/java/backend.py +97 -0
- package/adl_drift/lang/java/cfg.py +1424 -0
- package/adl_drift/lang/java/config.py +707 -0
- package/adl_drift/lang/java/detectors/__init__.py +29 -0
- package/adl_drift/lang/java/detectors/additional.py +445 -0
- package/adl_drift/lang/java/detectors/base.py +154 -0
- package/adl_drift/lang/java/detectors/data_access.py +148 -0
- package/adl_drift/lang/java/detectors/imports.py +53 -0
- package/adl_drift/lang/java/detectors/routes.py +159 -0
- package/adl_drift/lang/java/detectors/security.py +386 -0
- package/adl_drift/lang/java/detectors/tests.py +74 -0
- package/adl_drift/lang/java/extractor.py +582 -0
- package/adl_drift/lang/java/ids.py +99 -0
- package/adl_drift/lang/java/parser.py +49 -0
- package/adl_drift/lang/java/resolution/__init__.py +1 -0
- package/adl_drift/lang/java/resolution/constants.py +1619 -0
- package/adl_drift/lang/java/resolution/constructors.py +278 -0
- package/adl_drift/lang/java/resolution/context.py +305 -0
- package/adl_drift/lang/java/resolution/dispatch.py +540 -0
- package/adl_drift/lang/java/resolution/functional.py +1318 -0
- package/adl_drift/lang/java/resolution/generated.py +95 -0
- package/adl_drift/lang/java/resolution/model.py +153 -0
- package/adl_drift/lang/java/resolution/orchestration.py +866 -0
- package/adl_drift/lang/java/resolution/overloads.py +1275 -0
- package/adl_drift/lang/java/resolution/scopes.py +1111 -0
- package/adl_drift/lang/java/resolution/types.py +2947 -0
- package/adl_drift/lang/java/resolver.py +1401 -0
- package/adl_drift/lang/java/scopes.py +69 -0
- package/adl_drift/lang/java/taint.py +500 -0
- package/adl_drift/lang/java/type_provenance.py +342 -0
- package/adl_drift/lang/python/__init__.py +8 -0
- package/adl_drift/lang/python/backend.py +94 -0
- package/adl_drift/lang/python/cfg.py +537 -0
- package/adl_drift/lang/python/extractor.py +454 -0
- package/adl_drift/lang/python/parser.py +41 -0
- package/adl_drift/lang/python/resolver.py +1789 -0
- package/adl_drift/lang/python/taint.py +365 -0
- package/adl_drift/lang/registry.py +117 -0
- package/adl_drift/parser/__init__.py +1 -0
- package/adl_drift/parser/cache.py +38 -0
- package/adl_drift/parser/core.py +33 -0
- package/adl_drift/parser/extractor.py +17 -0
- package/adl_drift/parser/literal_eval.py +326 -0
- package/adl_drift/scanner/__init__.py +1 -0
- package/adl_drift/scanner/discovery.py +338 -0
- package/adl_drift/scanner/incremental.py +261 -0
- package/adl_drift/scanner/parallel.py +224 -0
- package/adl_drift/server/__init__.py +1 -0
- package/adl_drift/server/handlers.py +2849 -0
- package/adl_drift/server/mcp_server.py +127 -0
- package/adl_drift/server/mcp_stdio.py +335 -0
- package/adl_drift/server/protocol.py +71 -0
- package/adl_drift/server/static/index.html +696 -0
- package/adl_drift/server/web.py +350 -0
- package/adl_drift/utils/__init__.py +1 -0
- package/adl_drift/utils/config.py +77 -0
- package/adl_drift/utils/fswalk.py +52 -0
- package/adl_drift/utils/types.py +345 -0
- package/bin/adl-drift.mjs +35 -0
- package/bin/adl-toolkit.mjs +488 -0
- package/package.json +29 -4
- package/pyproject.toml +49 -0
- package/scripts/install_toolkit.py +3584 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Test framework detector — pytest + unittest."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import List
|
|
7
|
+
|
|
8
|
+
from tree_sitter import Node, Tree
|
|
9
|
+
|
|
10
|
+
from adl_drift.detectors.base import BaseDetector
|
|
11
|
+
from adl_drift.utils.types import Pattern, PatternCategory
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TestDetector(BaseDetector):
|
|
15
|
+
"""Flags test functions, fixtures, and unittest.TestCase classes."""
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def category(self) -> PatternCategory:
|
|
19
|
+
return PatternCategory.TESTS
|
|
20
|
+
|
|
21
|
+
def detect(self, tree: Tree, source_bytes: bytes, file_path: Path) -> List[Pattern]:
|
|
22
|
+
patterns: List[Pattern] = []
|
|
23
|
+
in_test_file = self._is_test_file(file_path)
|
|
24
|
+
|
|
25
|
+
for node in self._walk(tree.root_node):
|
|
26
|
+
if node.type == "function_definition":
|
|
27
|
+
name = self._name(node, source_bytes)
|
|
28
|
+
if not name:
|
|
29
|
+
continue
|
|
30
|
+
decorators = self._decorators(node, source_bytes)
|
|
31
|
+
|
|
32
|
+
if any("fixture" in d for d in decorators):
|
|
33
|
+
patterns.append(self._make_pattern(
|
|
34
|
+
name="pytest_fixture",
|
|
35
|
+
description=f"pytest fixture: {name}",
|
|
36
|
+
node=node, file_path=file_path, confidence=0.95,
|
|
37
|
+
metadata={"framework": "pytest", "function": name},
|
|
38
|
+
))
|
|
39
|
+
elif name.startswith("test_") and in_test_file:
|
|
40
|
+
patterns.append(self._make_pattern(
|
|
41
|
+
name="pytest_test",
|
|
42
|
+
description=f"pytest test function: {name}",
|
|
43
|
+
node=node, file_path=file_path, confidence=0.9,
|
|
44
|
+
metadata={"framework": "pytest", "function": name},
|
|
45
|
+
))
|
|
46
|
+
elif node.type == "class_definition":
|
|
47
|
+
bases = self._superclass_text(node, source_bytes)
|
|
48
|
+
if any("TestCase" in b for b in bases):
|
|
49
|
+
name = self._name(node, source_bytes) or "<anon>"
|
|
50
|
+
patterns.append(self._make_pattern(
|
|
51
|
+
name="unittest_testcase",
|
|
52
|
+
description=f"unittest.TestCase class: {name}",
|
|
53
|
+
node=node, file_path=file_path, confidence=0.95,
|
|
54
|
+
metadata={"framework": "unittest", "class": name,
|
|
55
|
+
"bases": bases},
|
|
56
|
+
))
|
|
57
|
+
return patterns
|
|
58
|
+
|
|
59
|
+
def _name(self, node: Node, source: bytes):
|
|
60
|
+
nn = node.child_by_field_name("name")
|
|
61
|
+
return self.parser.node_text(nn, source) if nn else None
|
|
62
|
+
|
|
63
|
+
def _decorators(self, node: Node, source: bytes) -> List[str]:
|
|
64
|
+
out: List[str] = []
|
|
65
|
+
sib = node.prev_sibling
|
|
66
|
+
while sib and sib.type == "decorator":
|
|
67
|
+
out.append(self.parser.node_text(sib, source).strip())
|
|
68
|
+
sib = sib.prev_sibling
|
|
69
|
+
if node.parent and node.parent.type == "decorated_definition":
|
|
70
|
+
for c in node.parent.children:
|
|
71
|
+
if c.type == "decorator":
|
|
72
|
+
txt = self.parser.node_text(c, source).strip()
|
|
73
|
+
if txt not in out:
|
|
74
|
+
out.append(txt)
|
|
75
|
+
return out
|
|
76
|
+
|
|
77
|
+
def _superclass_text(self, node: Node, source: bytes) -> List[str]:
|
|
78
|
+
sc = node.child_by_field_name("superclasses")
|
|
79
|
+
if not sc:
|
|
80
|
+
return []
|
|
81
|
+
out: List[str] = []
|
|
82
|
+
for c in sc.children:
|
|
83
|
+
if c.type in ("identifier", "attribute"):
|
|
84
|
+
out.append(self.parser.node_text(c, source).strip())
|
|
85
|
+
return out
|
|
86
|
+
|
|
87
|
+
@staticmethod
|
|
88
|
+
def _is_test_file(file_path: Path) -> bool:
|
|
89
|
+
n = file_path.name
|
|
90
|
+
return n.startswith("test_") or n.endswith("_test.py") or "tests" in file_path.parts
|
|
91
|
+
|
|
92
|
+
def _walk(self, node: Node):
|
|
93
|
+
yield node
|
|
94
|
+
for child in node.children:
|
|
95
|
+
yield from self._walk(child)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Type-annotation coverage detector.
|
|
2
|
+
|
|
3
|
+
Per-file pattern: ratio of annotated params/returns. One Pattern per scanned
|
|
4
|
+
file with coverage % in metadata. Depends on P1.3 (Symbol.param_types /
|
|
5
|
+
return_type populated).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import List
|
|
12
|
+
|
|
13
|
+
from tree_sitter import Node, Tree
|
|
14
|
+
|
|
15
|
+
from adl_drift.detectors.base import BaseDetector
|
|
16
|
+
from adl_drift.utils.types import Pattern, PatternCategory
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class TypeConventionDetector(BaseDetector):
|
|
20
|
+
"""Computes type-annotation coverage for a file."""
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def category(self) -> PatternCategory:
|
|
24
|
+
return PatternCategory.TYPES
|
|
25
|
+
|
|
26
|
+
def detect(self, tree: Tree, source_bytes: bytes, file_path: Path) -> List[Pattern]:
|
|
27
|
+
total_params = 0
|
|
28
|
+
typed_params = 0
|
|
29
|
+
total_funcs = 0
|
|
30
|
+
typed_returns = 0
|
|
31
|
+
|
|
32
|
+
for node in self._walk(tree.root_node):
|
|
33
|
+
if node.type != "function_definition":
|
|
34
|
+
continue
|
|
35
|
+
total_funcs += 1
|
|
36
|
+
params_node = node.child_by_field_name("parameters")
|
|
37
|
+
if params_node is not None:
|
|
38
|
+
for child in params_node.children:
|
|
39
|
+
ct = child.type
|
|
40
|
+
if ct == "identifier":
|
|
41
|
+
text = self.parser.node_text(child, source_bytes)
|
|
42
|
+
if text in ("self", "cls"):
|
|
43
|
+
continue
|
|
44
|
+
total_params += 1
|
|
45
|
+
elif ct == "default_parameter":
|
|
46
|
+
total_params += 1
|
|
47
|
+
elif ct in ("typed_parameter", "typed_default_parameter"):
|
|
48
|
+
total_params += 1
|
|
49
|
+
typed_params += 1
|
|
50
|
+
if node.child_by_field_name("return_type") is not None:
|
|
51
|
+
typed_returns += 1
|
|
52
|
+
|
|
53
|
+
if total_funcs == 0:
|
|
54
|
+
return []
|
|
55
|
+
|
|
56
|
+
param_cov = typed_params / total_params if total_params else 1.0
|
|
57
|
+
return_cov = typed_returns / total_funcs
|
|
58
|
+
|
|
59
|
+
return [self._make_pattern(
|
|
60
|
+
name="type_coverage",
|
|
61
|
+
description=(
|
|
62
|
+
f"type coverage — params {typed_params}/{total_params} "
|
|
63
|
+
f"({param_cov:.0%}), returns {typed_returns}/{total_funcs} "
|
|
64
|
+
f"({return_cov:.0%})"
|
|
65
|
+
),
|
|
66
|
+
node=tree.root_node, file_path=file_path,
|
|
67
|
+
confidence=0.95,
|
|
68
|
+
metadata={
|
|
69
|
+
"total_params": total_params,
|
|
70
|
+
"typed_params": typed_params,
|
|
71
|
+
"param_coverage": round(param_cov, 3),
|
|
72
|
+
"total_functions": total_funcs,
|
|
73
|
+
"typed_returns": typed_returns,
|
|
74
|
+
"return_coverage": round(return_cov, 3),
|
|
75
|
+
},
|
|
76
|
+
)]
|
|
77
|
+
|
|
78
|
+
def _walk(self, node: Node):
|
|
79
|
+
yield node
|
|
80
|
+
for child in node.children:
|
|
81
|
+
yield from self._walk(child)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Call graph and dependency analysis."""
|