adl-toolkit 0.0.0-stage → 0.1.1

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.
Files changed (104) hide show
  1. package/INSTALL_TOOLKIT.md +163 -0
  2. package/README.md +269 -2
  3. package/adl_drift/__init__.py +6 -0
  4. package/adl_drift/cli/__init__.py +1 -0
  5. package/adl_drift/cli/commands.py +993 -0
  6. package/adl_drift/cli/main.py +744 -0
  7. package/adl_drift/cortex/__init__.py +1 -0
  8. package/adl_drift/cortex/embeddings.py +96 -0
  9. package/adl_drift/cortex/memory.py +1749 -0
  10. package/adl_drift/cortex/patterns.py +89 -0
  11. package/adl_drift/cortex/telemetry.py +192 -0
  12. package/adl_drift/detectors/__init__.py +1 -0
  13. package/adl_drift/detectors/async_patterns.py +115 -0
  14. package/adl_drift/detectors/base.py +121 -0
  15. package/adl_drift/detectors/config.py +122 -0
  16. package/adl_drift/detectors/data_access.py +128 -0
  17. package/adl_drift/detectors/decorators.py +122 -0
  18. package/adl_drift/detectors/dynamic_patterns.py +260 -0
  19. package/adl_drift/detectors/errors.py +134 -0
  20. package/adl_drift/detectors/imports.py +132 -0
  21. package/adl_drift/detectors/logging_patterns.py +108 -0
  22. package/adl_drift/detectors/routes.py +257 -0
  23. package/adl_drift/detectors/security.py +236 -0
  24. package/adl_drift/detectors/taint.py +406 -0
  25. package/adl_drift/detectors/tests.py +95 -0
  26. package/adl_drift/detectors/type_conventions.py +81 -0
  27. package/adl_drift/graph/__init__.py +1 -0
  28. package/adl_drift/graph/builder.py +2670 -0
  29. package/adl_drift/graph/cfg.py +461 -0
  30. package/adl_drift/graph/cluster.py +40 -0
  31. package/adl_drift/graph/edges.py +86 -0
  32. package/adl_drift/graph/node.py +90 -0
  33. package/adl_drift/harness/__init__.py +10 -0
  34. package/adl_drift/harness/setup.py +832 -0
  35. package/adl_drift/harness/skills/drift-antigravity.md +19 -0
  36. package/adl_drift/harness/skills/drift-generic.md +19 -0
  37. package/adl_drift/harness/skills/drift.md +25 -0
  38. package/adl_drift/harness/watch.py +84 -0
  39. package/adl_drift/lang/__init__.py +8 -0
  40. package/adl_drift/lang/base.py +464 -0
  41. package/adl_drift/lang/java/__init__.py +6 -0
  42. package/adl_drift/lang/java/backend.py +97 -0
  43. package/adl_drift/lang/java/cfg.py +1424 -0
  44. package/adl_drift/lang/java/config.py +707 -0
  45. package/adl_drift/lang/java/detectors/__init__.py +29 -0
  46. package/adl_drift/lang/java/detectors/additional.py +445 -0
  47. package/adl_drift/lang/java/detectors/base.py +154 -0
  48. package/adl_drift/lang/java/detectors/data_access.py +148 -0
  49. package/adl_drift/lang/java/detectors/imports.py +53 -0
  50. package/adl_drift/lang/java/detectors/routes.py +159 -0
  51. package/adl_drift/lang/java/detectors/security.py +386 -0
  52. package/adl_drift/lang/java/detectors/tests.py +74 -0
  53. package/adl_drift/lang/java/extractor.py +582 -0
  54. package/adl_drift/lang/java/ids.py +99 -0
  55. package/adl_drift/lang/java/parser.py +49 -0
  56. package/adl_drift/lang/java/resolution/__init__.py +1 -0
  57. package/adl_drift/lang/java/resolution/constants.py +1619 -0
  58. package/adl_drift/lang/java/resolution/constructors.py +278 -0
  59. package/adl_drift/lang/java/resolution/context.py +305 -0
  60. package/adl_drift/lang/java/resolution/dispatch.py +540 -0
  61. package/adl_drift/lang/java/resolution/functional.py +1318 -0
  62. package/adl_drift/lang/java/resolution/generated.py +95 -0
  63. package/adl_drift/lang/java/resolution/model.py +153 -0
  64. package/adl_drift/lang/java/resolution/orchestration.py +866 -0
  65. package/adl_drift/lang/java/resolution/overloads.py +1275 -0
  66. package/adl_drift/lang/java/resolution/scopes.py +1111 -0
  67. package/adl_drift/lang/java/resolution/types.py +2947 -0
  68. package/adl_drift/lang/java/resolver.py +1401 -0
  69. package/adl_drift/lang/java/scopes.py +69 -0
  70. package/adl_drift/lang/java/taint.py +500 -0
  71. package/adl_drift/lang/java/type_provenance.py +342 -0
  72. package/adl_drift/lang/python/__init__.py +8 -0
  73. package/adl_drift/lang/python/backend.py +94 -0
  74. package/adl_drift/lang/python/cfg.py +537 -0
  75. package/adl_drift/lang/python/extractor.py +454 -0
  76. package/adl_drift/lang/python/parser.py +41 -0
  77. package/adl_drift/lang/python/resolver.py +1789 -0
  78. package/adl_drift/lang/python/taint.py +365 -0
  79. package/adl_drift/lang/registry.py +117 -0
  80. package/adl_drift/parser/__init__.py +1 -0
  81. package/adl_drift/parser/cache.py +38 -0
  82. package/adl_drift/parser/core.py +33 -0
  83. package/adl_drift/parser/extractor.py +17 -0
  84. package/adl_drift/parser/literal_eval.py +326 -0
  85. package/adl_drift/scanner/__init__.py +1 -0
  86. package/adl_drift/scanner/discovery.py +338 -0
  87. package/adl_drift/scanner/incremental.py +261 -0
  88. package/adl_drift/scanner/parallel.py +224 -0
  89. package/adl_drift/server/__init__.py +1 -0
  90. package/adl_drift/server/handlers.py +2849 -0
  91. package/adl_drift/server/mcp_server.py +127 -0
  92. package/adl_drift/server/mcp_stdio.py +335 -0
  93. package/adl_drift/server/protocol.py +71 -0
  94. package/adl_drift/server/static/index.html +696 -0
  95. package/adl_drift/server/web.py +350 -0
  96. package/adl_drift/utils/__init__.py +1 -0
  97. package/adl_drift/utils/config.py +77 -0
  98. package/adl_drift/utils/fswalk.py +52 -0
  99. package/adl_drift/utils/types.py +345 -0
  100. package/bin/adl-drift.mjs +35 -0
  101. package/bin/adl-toolkit.mjs +488 -0
  102. package/package.json +29 -4
  103. package/pyproject.toml +49 -0
  104. package/scripts/install_toolkit.py +3584 -0
@@ -0,0 +1,95 @@
1
+ """Synthetic Java members required by language and annotation semantics."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Dict, List
6
+
7
+ from adl_drift.graph.edges import EdgeType, GraphEdge
8
+ from adl_drift.graph.node import GraphNode, NodeType
9
+
10
+
11
+ class GeneratedMembersMixin:
12
+ def _ensure_generated_member_stubs(self) -> Dict[str, List[str]]:
13
+ """Materialize mandatory enum methods and record accessors.
14
+
15
+ The extractor correctly records only source declarations, while enum
16
+ ``values``/``valueOf`` and record component accessors are generated by
17
+ the compiler and therefore have no CST method declaration. Resolution
18
+ still needs real FUNCTION targets, so create only these language-
19
+ mandated members, with deterministic ids and normal CONTAINS edges.
20
+ """
21
+ created: Dict[str, List[str]] = {}
22
+ type_nodes = [
23
+ node for node in list(self.builder._nodes.values())
24
+ if node.language == "java"
25
+ and node.node_type == NodeType.CLASS
26
+ and node.metadata.get("java_type_kind") in {"enum", "record"}
27
+ ]
28
+ for type_node in type_nodes:
29
+ kind = type_node.metadata.get("java_type_kind")
30
+ # Keep the enclosing type path for nested enums. Returning only
31
+ # `Status[]` from `EventPublication.Status.values()` loses the
32
+ # owner and can become ambiguous (or unresolvable) at callers in
33
+ # another nested class. The portion after the module separator is
34
+ # the source qualname accepted by `_resolve_declared_type`.
35
+ enum_type = type_node.id.rsplit(":", 1)[-1]
36
+ methods = (
37
+ [("values", [], f"{enum_type}[]", True),
38
+ ("valueOf", ["String"], enum_type, True)]
39
+ if kind == "enum"
40
+ else [
41
+ (name, [], str(component_type), False)
42
+ for name, component_type in dict(
43
+ type_node.metadata.get("record_components", {})
44
+ ).items()
45
+ ]
46
+ )
47
+ for name, params, return_type, is_static in methods:
48
+ signature_key = f"({','.join(params)})"
49
+ stub_id = f"{type_node.id}.{name}{signature_key}"
50
+ self._add_generated_member_stub(
51
+ type_node, stub_id, name, params, return_type, is_static,
52
+ )
53
+ created[stub_id] = params
54
+ return created
55
+
56
+ def _add_generated_member_stub(
57
+ self, type_node: GraphNode, stub_id: str, name: str,
58
+ params: List[str], return_type: str, is_static: bool,
59
+ ) -> None:
60
+ if stub_id not in self.builder._nodes:
61
+ signature_key = f"({','.join(params)})"
62
+ marker = (
63
+ "java_synthetic_enum_method"
64
+ if type_node.metadata.get("java_type_kind") == "enum"
65
+ else "java_synthetic_record_accessor"
66
+ )
67
+ self.builder._add_node(GraphNode(
68
+ id=stub_id,
69
+ name=name,
70
+ node_type=NodeType.FUNCTION,
71
+ module=type_node.module,
72
+ file_path=type_node.file_path,
73
+ line=type_node.line,
74
+ column=type_node.column,
75
+ signature=f"{return_type} {name}{signature_key}",
76
+ contained_in=type_node.name,
77
+ language="java",
78
+ metadata={
79
+ "java_is_static": is_static,
80
+ "java_is_abstract": False,
81
+ "java_return_type": return_type,
82
+ marker: True,
83
+ },
84
+ ))
85
+ self.builder._add_edge(GraphEdge(
86
+ source=type_node.id,
87
+ target=stub_id,
88
+ edge_type=EdgeType.CONTAINS,
89
+ file_path=type_node.file_path,
90
+ line=type_node.line,
91
+ column=type_node.column,
92
+ strategy="java_resolver",
93
+ ))
94
+
95
+ # ------------------------------------------------------------------ CST utilities
@@ -0,0 +1,153 @@
1
+ """Shared data models for Java call resolution.
2
+
3
+ These types deliberately contain no graph traversal or resolution policy. Keeping
4
+ them in a dependency-leaf module lets the resolver's specialized components share
5
+ context without importing the orchestration class.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from collections import defaultdict
11
+ from typing import Dict, FrozenSet, List, NamedTuple, Optional, Tuple
12
+
13
+
14
+ class SAMDescriptor(NamedTuple):
15
+ params: List[str]
16
+ return_type: Optional[str]
17
+
18
+
19
+ class BeanCandidate(NamedTuple):
20
+ """One concrete Spring bean that can satisfy an injected interface."""
21
+
22
+ type_id: str
23
+ bean_name: Optional[str]
24
+ qualifiers: FrozenSet[str]
25
+ primary: bool
26
+ source: str
27
+
28
+
29
+ class InjectedFieldMetadata(NamedTuple):
30
+ """DI metadata inferred for a field from constructor or setter injection."""
31
+
32
+ qualifier: Optional[str]
33
+ source: str
34
+
35
+
36
+ class DINarrowResult(NamedTuple):
37
+ """Result of Spring DI narrowing for an injected interface receiver."""
38
+
39
+ target: Optional[str]
40
+ confidence: float
41
+ strategy: str
42
+
43
+
44
+ class ImportContext(NamedTuple):
45
+ """One source file's Java name-resolution context.
46
+
47
+ `enclosing_class_id` (optional, defaults to `None` for every pre-existing
48
+ construction site) is the CLASS node id whose body this context resolves
49
+ names *for* — the caller's own class for a call-site's argument context,
50
+ or a candidate method's declaring class for its parameter context. It
51
+ lets `_resolve_declared_type` look up a bare type name inherited from a
52
+ superclass/superinterface (JLS 6.4.1), which needs the referencing
53
+ class's own supertype chain, not just its module/imports."""
54
+
55
+ module: str
56
+ import_map: Dict[str, str]
57
+ wildcard_packages: List[str]
58
+ enclosing_class_id: Optional[str] = None
59
+
60
+
61
+ class LocalScopes:
62
+ """Lexically-scoped local-variable and allocation types for one body."""
63
+
64
+ __slots__ = ("_by_name", "_allocation_by_name", "_declared_by_name")
65
+
66
+ def __init__(self) -> None:
67
+ self._by_name: Dict[str, List[Tuple[str, int, int]]] = defaultdict(list)
68
+ self._allocation_by_name: Dict[
69
+ str, List[Tuple[str, int, int]]
70
+ ] = defaultdict(list)
71
+ self._declared_by_name: Dict[str, List[Tuple[int, int]]] = defaultdict(list)
72
+
73
+ def add(self, name: str, type_text: str, start: int, end: int) -> None:
74
+ # Multiple inference passes may describe the same declaration range:
75
+ # the early fluent-lambda pass can seed a placeholder type variable
76
+ # (`E`), then SAM target typing refines it to `SomeEvent`. The later,
77
+ # more contextual fact must replace—not sit behind—the placeholder.
78
+ self._by_name[name] = [
79
+ entry for entry in self._by_name[name]
80
+ if entry[1] != start or entry[2] != end
81
+ ]
82
+ self._by_name[name].append((type_text, start, end))
83
+ # A `var`'s first pass (`_collect_local_scopes`) may have recorded
84
+ # this exact range as "declared, type unknown" before a later,
85
+ # context-aware pass (`_collect_var_method_call_scopes`) resolved its
86
+ # real type — that placeholder must not outrank the now-known type
87
+ # in `type_at` just because it shares the same range.
88
+ if name in self._declared_by_name:
89
+ self._declared_by_name[name] = [
90
+ entry for entry in self._declared_by_name[name]
91
+ if entry[0] != start or entry[1] != end
92
+ ]
93
+
94
+ def type_at(self, name: str, offset: int) -> Optional[str]:
95
+ best_type: Optional[str] = None
96
+ best_start = -1
97
+ for type_text, start, end in self._by_name.get(name, ()):
98
+ # Equal starts arise when a later contextual pass refines the
99
+ # same lambda parameter with a slightly different end boundary.
100
+ # Iteration order is inference order, so the later fact wins.
101
+ if start <= offset <= end and start >= best_start:
102
+ best_type, best_start = type_text, start
103
+ for start, end in self._declared_by_name.get(name, ()):
104
+ # A declared-but-type-unknown entry (a lambda parameter whose
105
+ # SAM type couldn't be inferred, or a `var` local whose
106
+ # initializer type couldn't be resolved) still shadows an outer
107
+ # typed declaration of the same name when it is the innermost
108
+ # match — the outer type must not leak into a nested scope it
109
+ # isn't actually visible from (P4.4 shadow-detection fix).
110
+ if start <= offset <= end and start >= best_start:
111
+ best_type, best_start = None, start
112
+ return best_type
113
+
114
+ def add_declared(self, name: str, start: int, end: int) -> None:
115
+ """Record that `name` is a local whose type could not be resolved
116
+ (e.g. a `var` initialized from an unresolvable expression). `add()`
117
+ is skipped for these — there is no type to record — but callers that
118
+ only need "is this name shadowed by *some* local declaration here",
119
+ not its type, must still see it (P4.4 shadow-detection fix)."""
120
+ self._declared_by_name[name].append((start, end))
121
+
122
+ def declared_at(self, name: str, offset: int) -> bool:
123
+ return any(
124
+ start <= offset <= end
125
+ for start, end in self._declared_by_name.get(name, ())
126
+ )
127
+
128
+ def add_allocation(self, name: str, type_text: str, start: int, end: int) -> None:
129
+ self._allocation_by_name[name].append((type_text, start, end))
130
+
131
+ def allocation_type_at(self, name: str, offset: int) -> Optional[str]:
132
+ best_type: Optional[str] = None
133
+ best_start = -1
134
+ for type_text, start, end in self._allocation_by_name.get(name, ()):
135
+ if start <= offset <= end and start > best_start:
136
+ best_type, best_start = type_text, start
137
+ return best_type
138
+
139
+
140
+ class MethodFacts(NamedTuple):
141
+ """Immutable CST facts shared by one Java method body in one resolve run.
142
+
143
+ Call sites, method references, and scope declarations all used to require
144
+ separate body DFS walks. This object intentionally carries syntax facts
145
+ only: scope/type inference and target resolution keep their existing
146
+ semantics and mutable per-caller state.
147
+ """
148
+
149
+ call_sites: Tuple[Tuple[object, str], ...]
150
+ method_references: Tuple[object, ...]
151
+ assignment_expressions: Tuple[object, ...]
152
+ local_scope_nodes: Tuple[object, ...]
153
+ lambda_expressions: Tuple[object, ...]