sourcecode 2.6.2__py3-none-any.whl → 2.6.3__py3-none-any.whl
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.
Potentially problematic release.
This version of sourcecode might be problematic. Click here for more details.
- sourcecode/__init__.py +1 -1
- sourcecode/cli.py +81 -0
- sourcecode/mcp/registry.py +49 -1
- sourcecode/parse_cache.py +138 -0
- sourcecode/repository_ir.py +46 -12
- sourcecode/verify_edit.py +588 -0
- sourcecode/verify_rules.py +304 -0
- {sourcecode-2.6.2.dist-info → sourcecode-2.6.3.dist-info}/METADATA +1 -1
- {sourcecode-2.6.2.dist-info → sourcecode-2.6.3.dist-info}/RECORD +12 -9
- {sourcecode-2.6.2.dist-info → sourcecode-2.6.3.dist-info}/WHEEL +0 -0
- {sourcecode-2.6.2.dist-info → sourcecode-2.6.3.dist-info}/entry_points.txt +0 -0
- {sourcecode-2.6.2.dist-info → sourcecode-2.6.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
"""verify-edit Fase V5 — custom domain rules (the wide, defensive moat).
|
|
2
|
+
|
|
3
|
+
The generic axes (contract / @Transactional / security / bean-wiring) encode Spring
|
|
4
|
+
runtime semantics that a competitor could, with effort, replicate. This module lets a
|
|
5
|
+
*client* declare invariants of THEIR codebase and have ``verify-edit`` enforce them —
|
|
6
|
+
"no RestController touches a JPA DAO directly", "every /payroll endpoint requires role
|
|
7
|
+
X". Those rules live in the client's repo and no external tool knows them; that is what
|
|
8
|
+
makes the moat inreplicable.
|
|
9
|
+
|
|
10
|
+
Discipline (roadmap §7):
|
|
11
|
+
|
|
12
|
+
* **VAI (Vendor-Agnostic Inference).** Our evaluator NEVER branches on a proprietary
|
|
13
|
+
name. A selector reasons purely by *structure* — has-annotation, fqn-matches-regex,
|
|
14
|
+
symbol-kind, modifier, source-file-matches. The concrete names ("RestController",
|
|
15
|
+
"DaoJpa", "/payroll") arrive as config **data**; they are the client's evidence,
|
|
16
|
+
never a hardcoded ``if fqn == "com.acme.Foo"`` in this code.
|
|
17
|
+
* **Determinism.** ``evaluate`` is a pure function of the CIR. Violation ids are stable
|
|
18
|
+
so the verdict layer can diff HEAD vs working and report only what the edit *added*.
|
|
19
|
+
* **Zero-config over the existing graph.** No rules file ⇒ no rules ⇒ nothing blocks.
|
|
20
|
+
Rules run against the CIR that is already built; no new extraction.
|
|
21
|
+
|
|
22
|
+
Rule file (repo-root ``.ask/verify-rules.json``, fallback ``.sourcecode/verify-rules.json``):
|
|
23
|
+
|
|
24
|
+
{
|
|
25
|
+
"rules": [
|
|
26
|
+
{
|
|
27
|
+
"id": "no-controller-to-dao",
|
|
28
|
+
"description": "REST controllers must not touch a JPA DAO directly",
|
|
29
|
+
"kind": "forbidden_edge",
|
|
30
|
+
"edge_type": "injects",
|
|
31
|
+
"from": {"annotation": "@RestController"},
|
|
32
|
+
"to": {"fqn": ".*DaoJpa$"}
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "payroll-needs-role",
|
|
36
|
+
"description": "every /payroll endpoint requires the PAYROLL role",
|
|
37
|
+
"kind": "require_endpoint_security",
|
|
38
|
+
"path": "^/payroll",
|
|
39
|
+
"require": {"role": "PAYROLL"}
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
"""
|
|
44
|
+
from __future__ import annotations
|
|
45
|
+
|
|
46
|
+
import json
|
|
47
|
+
import re
|
|
48
|
+
from dataclasses import dataclass
|
|
49
|
+
from pathlib import Path
|
|
50
|
+
from typing import Any, Optional
|
|
51
|
+
|
|
52
|
+
RULES_SCHEMA = "verify-rules-v1"
|
|
53
|
+
_RULE_FILES = (".ask/verify-rules.json", ".sourcecode/verify-rules.json")
|
|
54
|
+
_EDGE_KIND = "forbidden_edge"
|
|
55
|
+
_ENDPOINT_KIND = "require_endpoint_security"
|
|
56
|
+
_KINDS = (_EDGE_KIND, _ENDPOINT_KIND)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class RulesError(ValueError):
|
|
60
|
+
"""The rules file exists but is malformed — verify-edit reports *unverified*, never
|
|
61
|
+
a silent pass (a broken ruleset must not read as "no violations")."""
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# ── selector: a pure structural predicate over one graph node ────────────────────
|
|
65
|
+
@dataclass(frozen=True)
|
|
66
|
+
class Selector:
|
|
67
|
+
"""Matches a graph node by structure only. Every present clause must hold (AND).
|
|
68
|
+
An empty selector matches every node. Names are data, never branched on by kind."""
|
|
69
|
+
|
|
70
|
+
annotation: Optional[str] = None # node.annotations contains this (exact, e.g. "@RestController")
|
|
71
|
+
fqn: Optional[str] = None # regex, fully matched against node fqn
|
|
72
|
+
symbol_kind: Optional[str] = None # exact node symbol_kind (class|interface|method|field)
|
|
73
|
+
modifier: Optional[str] = None # node.modifiers contains this (e.g. "public")
|
|
74
|
+
source_file: Optional[str] = None # regex, searched against node source_file
|
|
75
|
+
_fqn_re: Any = None
|
|
76
|
+
_sf_re: Any = None
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def from_dict(cls, d: Any, *, where: str) -> "Selector":
|
|
80
|
+
if d is None:
|
|
81
|
+
return cls()
|
|
82
|
+
if not isinstance(d, dict):
|
|
83
|
+
raise RulesError(f"{where}: selector must be an object, got {type(d).__name__}")
|
|
84
|
+
known = {"annotation", "fqn", "symbol_kind", "modifier", "source_file"}
|
|
85
|
+
unknown = set(d) - known
|
|
86
|
+
if unknown:
|
|
87
|
+
raise RulesError(f"{where}: unknown selector field(s) {sorted(unknown)}")
|
|
88
|
+
try:
|
|
89
|
+
fqn_re = re.compile(d["fqn"]) if d.get("fqn") else None
|
|
90
|
+
sf_re = re.compile(d["source_file"]) if d.get("source_file") else None
|
|
91
|
+
except re.error as exc:
|
|
92
|
+
raise RulesError(f"{where}: invalid regex ({exc})") from exc
|
|
93
|
+
return cls(
|
|
94
|
+
annotation=d.get("annotation"),
|
|
95
|
+
fqn=d.get("fqn"),
|
|
96
|
+
symbol_kind=d.get("symbol_kind"),
|
|
97
|
+
modifier=d.get("modifier"),
|
|
98
|
+
source_file=d.get("source_file"),
|
|
99
|
+
_fqn_re=fqn_re,
|
|
100
|
+
_sf_re=sf_re,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
def is_empty(self) -> bool:
|
|
104
|
+
return not any((self.annotation, self.fqn, self.symbol_kind, self.modifier, self.source_file))
|
|
105
|
+
|
|
106
|
+
def matches(self, node: dict) -> bool:
|
|
107
|
+
if self.annotation is not None and self.annotation not in (node.get("annotations") or []):
|
|
108
|
+
return False
|
|
109
|
+
if self._fqn_re is not None and not self._fqn_re.fullmatch(node.get("fqn") or ""):
|
|
110
|
+
return False
|
|
111
|
+
if self.symbol_kind is not None and (node.get("symbol_kind") or "") != self.symbol_kind:
|
|
112
|
+
return False
|
|
113
|
+
if self.modifier is not None and self.modifier not in (node.get("modifiers") or []):
|
|
114
|
+
return False
|
|
115
|
+
if self._sf_re is not None and not self._sf_re.search(node.get("source_file") or ""):
|
|
116
|
+
return False
|
|
117
|
+
return True
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@dataclass(frozen=True)
|
|
121
|
+
class Violation:
|
|
122
|
+
rule_id: str
|
|
123
|
+
subject: str # the offending symbol / endpoint id
|
|
124
|
+
detail: str
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def id(self) -> str:
|
|
128
|
+
"""Stable across runs, so the verdict layer can diff HEAD vs working."""
|
|
129
|
+
return f"{self.rule_id}::{self.subject}"
|
|
130
|
+
|
|
131
|
+
def to_dict(self) -> dict:
|
|
132
|
+
return {"rule_id": self.rule_id, "subject": self.subject, "detail": self.detail}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
# ── rules ────────────────────────────────────────────────────────────────────────
|
|
136
|
+
@dataclass(frozen=True)
|
|
137
|
+
class ForbiddenEdgeRule:
|
|
138
|
+
"""No edge of ``edge_type`` may go from a node matching ``from_sel`` to a node
|
|
139
|
+
matching ``to_sel``. Reasons over the CIR edge list — structure, not names."""
|
|
140
|
+
|
|
141
|
+
id: str
|
|
142
|
+
description: str
|
|
143
|
+
edge_type: str
|
|
144
|
+
from_sel: Selector
|
|
145
|
+
to_sel: Selector
|
|
146
|
+
|
|
147
|
+
def evaluate(self, cir: Any) -> list[Violation]:
|
|
148
|
+
nodes = {n.get("fqn"): n for n in _nodes(cir) if n.get("fqn")}
|
|
149
|
+
owner = _owner_type_map(cir) # member fqn → declaring type fqn
|
|
150
|
+
out: list[Violation] = []
|
|
151
|
+
for e in cir.call_graph:
|
|
152
|
+
if e.get("type") != self.edge_type:
|
|
153
|
+
continue
|
|
154
|
+
src, dst = e.get("from"), e.get("to")
|
|
155
|
+
fn, tn = nodes.get(src), nodes.get(dst)
|
|
156
|
+
if fn is None or tn is None:
|
|
157
|
+
continue # an endpoint of the edge is outside the graph — cannot judge
|
|
158
|
+
if self._endpoint_matches(self.from_sel, fn, nodes, owner) and \
|
|
159
|
+
self._endpoint_matches(self.to_sel, tn, nodes, owner):
|
|
160
|
+
out.append(Violation(
|
|
161
|
+
self.id, f"{src} -{self.edge_type}-> {dst}",
|
|
162
|
+
f"forbidden {self.edge_type} edge {src} → {dst}",
|
|
163
|
+
))
|
|
164
|
+
return out
|
|
165
|
+
|
|
166
|
+
@staticmethod
|
|
167
|
+
def _endpoint_matches(sel: Selector, node: dict, nodes: dict, owner: dict) -> bool:
|
|
168
|
+
"""Selector matches an edge endpoint iff it matches that node OR the type that
|
|
169
|
+
declares it. Edges are recorded at member granularity (a field's ``injects``, a
|
|
170
|
+
method's ``calls``) but client invariants are usually stated at type granularity
|
|
171
|
+
("no RestController touches a DAO"), so the annotation/kind on the owning class
|
|
172
|
+
must count. Purely structural — the owner is resolved via ``contained_in``."""
|
|
173
|
+
if sel.matches(node):
|
|
174
|
+
return True
|
|
175
|
+
owner_fqn = owner.get(node.get("fqn"))
|
|
176
|
+
owner_node = nodes.get(owner_fqn) if owner_fqn else None
|
|
177
|
+
return owner_node is not None and sel.matches(owner_node)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@dataclass(frozen=True)
|
|
181
|
+
class RequireEndpointSecurityRule:
|
|
182
|
+
"""Every endpoint whose path matches ``path_re`` must carry the required policy
|
|
183
|
+
and/or role. A missing/weaker policy on a matched endpoint is a violation."""
|
|
184
|
+
|
|
185
|
+
id: str
|
|
186
|
+
description: str
|
|
187
|
+
path_re: Any
|
|
188
|
+
require_policy: Optional[str]
|
|
189
|
+
require_role: Optional[str]
|
|
190
|
+
|
|
191
|
+
def evaluate(self, cir: Any) -> list[Violation]:
|
|
192
|
+
out: list[Violation] = []
|
|
193
|
+
for ep in cir.endpoints:
|
|
194
|
+
if not self.path_re.search(ep.path or ""):
|
|
195
|
+
continue
|
|
196
|
+
sec = ep.security
|
|
197
|
+
missing: list[str] = []
|
|
198
|
+
if self.require_policy is not None and (sec is None or sec.policy != self.require_policy):
|
|
199
|
+
got = repr(sec.policy) if sec else "None"
|
|
200
|
+
missing.append(f"policy={self.require_policy!r} (got {got})")
|
|
201
|
+
if self.require_role is not None and (sec is None or self.require_role not in (sec.effective_roles or [])):
|
|
202
|
+
missing.append(f"role={self.require_role!r}")
|
|
203
|
+
if missing:
|
|
204
|
+
out.append(Violation(
|
|
205
|
+
self.id, ep.id,
|
|
206
|
+
f"endpoint {ep.method} {ep.path} missing required {', '.join(missing)}",
|
|
207
|
+
))
|
|
208
|
+
return out
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
Rule = Any # ForbiddenEdgeRule | RequireEndpointSecurityRule
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _nodes(cir: Any) -> list[dict]:
|
|
215
|
+
return cir._raw_ir.get("graph", {}).get("nodes", []) or []
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _owner_type_map(cir: Any) -> dict[str, str]:
|
|
219
|
+
"""member fqn → declaring type fqn, from ``contained_in`` edges (field/method → class).
|
|
220
|
+
Lets a type-level selector match an edge endpoint recorded at member granularity."""
|
|
221
|
+
return {e["from"]: e["to"] for e in cir.call_graph
|
|
222
|
+
if e.get("type") == "contained_in" and e.get("from") and e.get("to")}
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
# ── loading / parsing ────────────────────────────────────────────────────────────
|
|
226
|
+
def _rule_from_dict(d: Any, idx: int) -> Rule:
|
|
227
|
+
where = f"rule[{idx}]"
|
|
228
|
+
if not isinstance(d, dict):
|
|
229
|
+
raise RulesError(f"{where}: must be an object")
|
|
230
|
+
rid = d.get("id")
|
|
231
|
+
if not rid or not isinstance(rid, str):
|
|
232
|
+
raise RulesError(f"{where}: missing string 'id'")
|
|
233
|
+
kind = d.get("kind")
|
|
234
|
+
if kind not in _KINDS:
|
|
235
|
+
raise RulesError(f"{where} ({rid}): 'kind' must be one of {list(_KINDS)}, got {kind!r}")
|
|
236
|
+
desc = d.get("description") or ""
|
|
237
|
+
|
|
238
|
+
if kind == _EDGE_KIND:
|
|
239
|
+
edge_type = d.get("edge_type")
|
|
240
|
+
if not edge_type or not isinstance(edge_type, str):
|
|
241
|
+
raise RulesError(f"{where} ({rid}): forbidden_edge needs a string 'edge_type'")
|
|
242
|
+
from_sel = Selector.from_dict(d.get("from"), where=f"{where} ({rid}).from")
|
|
243
|
+
to_sel = Selector.from_dict(d.get("to"), where=f"{where} ({rid}).to")
|
|
244
|
+
if from_sel.is_empty() and to_sel.is_empty():
|
|
245
|
+
raise RulesError(f"{where} ({rid}): 'from' and 'to' cannot both be empty (would ban all edges)")
|
|
246
|
+
return ForbiddenEdgeRule(rid, desc, edge_type, from_sel, to_sel)
|
|
247
|
+
|
|
248
|
+
# require_endpoint_security
|
|
249
|
+
path = d.get("path")
|
|
250
|
+
if not path or not isinstance(path, str):
|
|
251
|
+
raise RulesError(f"{where} ({rid}): require_endpoint_security needs a string 'path' regex")
|
|
252
|
+
try:
|
|
253
|
+
path_re = re.compile(path)
|
|
254
|
+
except re.error as exc:
|
|
255
|
+
raise RulesError(f"{where} ({rid}): invalid 'path' regex ({exc})") from exc
|
|
256
|
+
req = d.get("require")
|
|
257
|
+
if not isinstance(req, dict) or not (req.get("policy") or req.get("role")):
|
|
258
|
+
raise RulesError(f"{where} ({rid}): 'require' must set at least one of 'policy' / 'role'")
|
|
259
|
+
return RequireEndpointSecurityRule(rid, desc, path_re, req.get("policy"), req.get("role"))
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def parse_rules(raw: Any) -> list[Rule]:
|
|
263
|
+
"""Parse an already-decoded rules document into rule objects. Raises RulesError
|
|
264
|
+
on any malformation (duplicate ids included) so the axis degrades to *unverified*."""
|
|
265
|
+
if not isinstance(raw, dict) or "rules" not in raw:
|
|
266
|
+
raise RulesError("rules file must be an object with a 'rules' array")
|
|
267
|
+
rules_raw = raw["rules"]
|
|
268
|
+
if not isinstance(rules_raw, list):
|
|
269
|
+
raise RulesError("'rules' must be an array")
|
|
270
|
+
rules = [_rule_from_dict(d, i) for i, d in enumerate(rules_raw)]
|
|
271
|
+
seen: set[str] = set()
|
|
272
|
+
for r in rules:
|
|
273
|
+
if r.id in seen:
|
|
274
|
+
raise RulesError(f"duplicate rule id {r.id!r}")
|
|
275
|
+
seen.add(r.id)
|
|
276
|
+
return rules
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def rules_path(root: Path) -> Optional[Path]:
|
|
280
|
+
for rel in _RULE_FILES:
|
|
281
|
+
p = root / rel
|
|
282
|
+
if p.is_file():
|
|
283
|
+
return p
|
|
284
|
+
return None
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def load_rules(root: Path) -> list[Rule]:
|
|
288
|
+
"""Load the repo's custom rules. Absent file ⇒ ``[]`` (zero-config, nothing blocks).
|
|
289
|
+
Present-but-broken ⇒ RulesError (caller degrades to unverified — never a silent pass)."""
|
|
290
|
+
p = rules_path(Path(root))
|
|
291
|
+
if p is None:
|
|
292
|
+
return []
|
|
293
|
+
try:
|
|
294
|
+
raw = json.loads(p.read_text(encoding="utf-8"))
|
|
295
|
+
except (OSError, json.JSONDecodeError) as exc:
|
|
296
|
+
raise RulesError(f"could not read {p}: {exc}") from exc
|
|
297
|
+
return parse_rules(raw)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def evaluate_all(rules: list[Rule], cir: Any) -> list[Violation]:
|
|
301
|
+
out: list[Violation] = []
|
|
302
|
+
for r in rules:
|
|
303
|
+
out.extend(r.evaluate(cir))
|
|
304
|
+
return out
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
sourcecode/__init__.py,sha256=
|
|
1
|
+
sourcecode/__init__.py,sha256=inJYWLATw0hWFueMieZTLMVFkMV-j2YjHCR5sz3Dlh0,308
|
|
2
2
|
sourcecode/adaptive_scanner.py,sha256=yJBKjNpkY6bpueYJ2YnRezen3sYZDecEt7WaaNWdqug,9466
|
|
3
3
|
sourcecode/archetype.py,sha256=UrmiONHfqLl7UfGzjECoQBO28hcM_1rvzn0hCt0EtjU,35033
|
|
4
4
|
sourcecode/architectural_baseline.py,sha256=7QzJri4pbL3nzAn9gNutZW6mZR8HHD6H2C2H1EEwYPE,17904
|
|
@@ -13,7 +13,7 @@ sourcecode/canonical_ir.py,sha256=LdP_Ri3rl0M5p-MY0ypVvQwhq1WuUF1FmaHid7zyzEg,29
|
|
|
13
13
|
sourcecode/change_plan.py,sha256=MNgNyu4zrLGvBBaXPwyCh-T2ZGaUQX3Hm4W87uuhZ3w,7750
|
|
14
14
|
sourcecode/cir_graphs.py,sha256=9G0HHj1kw2325IDyzo2OpX73BNswEckecf4MZUXB4JM,12078
|
|
15
15
|
sourcecode/classifier.py,sha256=JBzPwSSrDG-tUHAbcKB678HRbjLpD-ohzbzzO62mgpo,20114
|
|
16
|
-
sourcecode/cli.py,sha256=
|
|
16
|
+
sourcecode/cli.py,sha256=vlr2fQDesPFaSGa6qLhG6GP7xWU6F-u0U_qjc03r14M,362994
|
|
17
17
|
sourcecode/code_notes_analyzer.py,sha256=EJemNCNc9Dn-1RZYu-aNbK0ELzmsyC4s6FdHi3XyNEI,9392
|
|
18
18
|
sourcecode/compare.py,sha256=jdePg0dNCxFpysiTGMsROL5XABLjy_zVUgVeySvdb0o,7514
|
|
19
19
|
sourcecode/confidence_analyzer.py,sha256=vnbPI-20FnHdjO6STxHW8fbaxmB4A7y58io63ibFZjc,21586
|
|
@@ -52,6 +52,7 @@ sourcecode/metrics_analyzer.py,sha256=m0ENgtqKeBL17kUIK3fmGkgo7UfXBNHxCMj0H_Y5K7
|
|
|
52
52
|
sourcecode/migrate_check.py,sha256=hsKNwipl-bSqRsYqG1O_CLbXpMlqtnN-Bif3hpHpnjg,108676
|
|
53
53
|
sourcecode/openapi_surface.py,sha256=BTt0K-woZbkbWTN77IkqeBm_Okag9owR0848fmot8sk,16207
|
|
54
54
|
sourcecode/output_budget.py,sha256=Js9yUlfQtPhqBl9R6wn_9UHVjjJc3GtLcqyfjf5t50Q,9869
|
|
55
|
+
sourcecode/parse_cache.py,sha256=EB2Eb23S3Z2UlyUIPetXv-Tu-1niB6eWVThmraLCyyA,5505
|
|
55
56
|
sourcecode/path_filters.py,sha256=qPKO7kRmVp2y9zjLNSuAVCbcpZrIInHE4QulLUlzPFI,10412
|
|
56
57
|
sourcecode/perf.py,sha256=GAcEoouPIlPMCQIcHNToxK6K3WdIR-lj9aFg4prOYJI,9743
|
|
57
58
|
sourcecode/pr_comment_renderer.py,sha256=KmcjMruhR44gjzMDJwjBSkWP9QEvh8xWBLyxzxoRbj0,14542
|
|
@@ -64,7 +65,7 @@ sourcecode/redactor.py,sha256=SB4hwIvg8h-hvcqKcDWaZvA-aSyn-at-BIRwa0tUv5E,3227
|
|
|
64
65
|
sourcecode/relevance_scorer.py,sha256=0AgEt4KrV73nioMqBgjhGjtY7L2C7L7cSyKtj3IKcrw,9408
|
|
65
66
|
sourcecode/rename_refactor.py,sha256=h6dNFlB9aZ_3q6heeHBkgXQeXaT03nvPSsYH6P8qxFg,12965
|
|
66
67
|
sourcecode/repo_classifier.py,sha256=FG1vaWKdWXsWdl-S8hjVMiTqcwgaRXkDyvK4rPcOGtQ,22681
|
|
67
|
-
sourcecode/repository_ir.py,sha256=
|
|
68
|
+
sourcecode/repository_ir.py,sha256=wD6ODf8nNijDIh01FWyPoY1jplpPLWvfKl_5rAvLVMQ,322680
|
|
68
69
|
sourcecode/ris.py,sha256=Hw8TakTQ6hku-Abf2k8954NwkrH_sP73_8wVH8x5khc,22079
|
|
69
70
|
sourcecode/runtime_classifier.py,sha256=uTAD6BDCiBLUZEDRfqk718kM4RTT_vAbfkcOI2_Xx58,18432
|
|
70
71
|
sourcecode/scanner.py,sha256=z3CV0rcGunu0Y8mpNgp07wI7nxT0pxw1BkXRRtI0Rpo,9609
|
|
@@ -88,6 +89,8 @@ sourcecode/tree_utils.py,sha256=8GAkIfQAsvtEudIeW1l4ooH_oRtrWR8cpJQJsEa_Pfw,2093
|
|
|
88
89
|
sourcecode/type_usage_surface.py,sha256=51IrKRQoIoRnlsiDjHnqpJBn2rc6E59aRhgS0HTzAF0,4428
|
|
89
90
|
sourcecode/validation_inference.py,sha256=KN3tEBQzxNinwlo7buer6lx5Kqdn71JAs_wuu44McfU,17475
|
|
90
91
|
sourcecode/validation_surface.py,sha256=A0-FVswL__XOcw92wvIwghwLq5AWtIm2kzO3RjEb1b4,27325
|
|
92
|
+
sourcecode/verify_edit.py,sha256=LFk5CRste11GwNUTUrTGxoQpmQhy4Qoqd7NdhGq1weA,26190
|
|
93
|
+
sourcecode/verify_rules.py,sha256=y7MXrjGDVed0uTY5P6hcP6AwvFGADDQ4uKanJfO9z5E,13273
|
|
91
94
|
sourcecode/version_check.py,sha256=CHp6ZxTIfo8kyHPCBgJA1uFC0xQCoXMuuOfrW8QTL8o,4942
|
|
92
95
|
sourcecode/workspace.py,sha256=X_6NmNnitvT3_38V-JDChydo_sR68s249hLFlrQskU0,8271
|
|
93
96
|
sourcecode/detectors/__init__.py,sha256=A0AACJFF6HWf_RgatNtWu3PUzstcKtIGM9f1PoFcJug,1987
|
|
@@ -113,7 +116,7 @@ sourcecode/detectors/terraform.py,sha256=cxORPR_zVLOJpHlh4e9JnFpkQsn_UnqMMom5yG6
|
|
|
113
116
|
sourcecode/detectors/tooling.py,sha256=8CKbtxwQoABP-WyBRNmdAmHDOvAH57AR1cF4UKuWEdQ,2074
|
|
114
117
|
sourcecode/mcp/__init__.py,sha256=XU4HfRGbdid8wdUA0x_4f7uKZD1z3mv_XUY_WU_T9Mw,179
|
|
115
118
|
sourcecode/mcp/orchestrator.py,sha256=diVoQgn24QmgPL3Ev8Sp6hsvh02OqY3MktHXOzrlodo,36525
|
|
116
|
-
sourcecode/mcp/registry.py,sha256=
|
|
119
|
+
sourcecode/mcp/registry.py,sha256=OCUCRLPRM176FCw850rF7dNh74f5Zj0h3a9lpxrf8mI,68171
|
|
117
120
|
sourcecode/mcp/runner.py,sha256=RGimCwhLyHxjCvH7q72y12HWEnATXZLC-NBWhkzlu0I,2852
|
|
118
121
|
sourcecode/mcp/server.py,sha256=yCp1YmmZ4hQE7KIHC8XcdxnToYCwSe9vxp4E-YMzVg0,63713
|
|
119
122
|
sourcecode/mcp/onboarding/__init__.py,sha256=sj2PWqEBmMc4zBNkomg89WtL0M6S7A9yb7_wAuSWNP4,66
|
|
@@ -145,8 +148,8 @@ sourcecode/telemetry/consent.py,sha256=LIAO9ohJZF8OuZwM4u1VWtALlYfTCCKq4wV3Vwc7i
|
|
|
145
148
|
sourcecode/telemetry/events.py,sha256=LtzYfaX9Ilckj5PTvAcTpDa9mLqDsYPDUiDkRa58piY,2580
|
|
146
149
|
sourcecode/telemetry/filters.py,sha256=NHa5T-6DaZduQPFuC34jOqHWQgSizM-Ygq8aZ4j19ng,5834
|
|
147
150
|
sourcecode/telemetry/transport.py,sha256=4gGHsq0WeY9VywEZXA3vUxykfiYnw9uuqfjAAec7F8o,1681
|
|
148
|
-
sourcecode-2.6.
|
|
149
|
-
sourcecode-2.6.
|
|
150
|
-
sourcecode-2.6.
|
|
151
|
-
sourcecode-2.6.
|
|
152
|
-
sourcecode-2.6.
|
|
151
|
+
sourcecode-2.6.3.dist-info/METADATA,sha256=83aYwUnc4szlY7QK1pyjw5suiM3HMkRKeRznWyPzd20,10851
|
|
152
|
+
sourcecode-2.6.3.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
153
|
+
sourcecode-2.6.3.dist-info/entry_points.txt,sha256=-JEAdChrK5We51kZcb7OaDcyil-dHBjBPL-NhuO-QY8,89
|
|
154
|
+
sourcecode-2.6.3.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
|
|
155
|
+
sourcecode-2.6.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|