sourcecode 0.43.0__py3-none-any.whl → 0.45.0__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.
- sourcecode/__init__.py +1 -1
- sourcecode/cli.py +71 -22
- sourcecode/context_scorer.py +404 -0
- sourcecode/contract_pipeline.py +22 -18
- sourcecode/detectors/heuristic.py +9 -1
- sourcecode/env_analyzer.py +2 -2
- sourcecode/prepare_context.py +27 -1
- sourcecode/ranking_engine.py +29 -7
- sourcecode/serializer.py +29 -4
- {sourcecode-0.43.0.dist-info → sourcecode-0.45.0.dist-info}/METADATA +1 -1
- {sourcecode-0.43.0.dist-info → sourcecode-0.45.0.dist-info}/RECORD +14 -13
- {sourcecode-0.43.0.dist-info → sourcecode-0.45.0.dist-info}/WHEEL +0 -0
- {sourcecode-0.43.0.dist-info → sourcecode-0.45.0.dist-info}/entry_points.txt +0 -0
- {sourcecode-0.43.0.dist-info → sourcecode-0.45.0.dist-info}/licenses/LICENSE +0 -0
sourcecode/__init__.py
CHANGED
sourcecode/cli.py
CHANGED
|
@@ -660,6 +660,21 @@ def main(
|
|
|
660
660
|
)
|
|
661
661
|
raise typer.Exit(code=1)
|
|
662
662
|
|
|
663
|
+
if symbol and mode not in ("contract", "standard"):
|
|
664
|
+
typer.echo(
|
|
665
|
+
f"Error: --symbol requires --mode contract or standard (got '{mode}'). "
|
|
666
|
+
"Symbol search uses the contract pipeline which does not run in raw mode.",
|
|
667
|
+
err=True,
|
|
668
|
+
)
|
|
669
|
+
raise typer.Exit(code=1)
|
|
670
|
+
|
|
671
|
+
if entrypoints_only and mode not in ("contract", "standard"):
|
|
672
|
+
typer.echo(
|
|
673
|
+
f"Error: --entrypoints-only requires --mode contract or standard (got '{mode}').",
|
|
674
|
+
err=True,
|
|
675
|
+
)
|
|
676
|
+
raise typer.Exit(code=1)
|
|
677
|
+
|
|
663
678
|
if dependency_depth > 0:
|
|
664
679
|
typer.echo(
|
|
665
680
|
f"[warning] --dependency-depth {dependency_depth} has no effect: "
|
|
@@ -1257,7 +1272,13 @@ def main(
|
|
|
1257
1272
|
eco_order = 0 if d.ecosystem == primary_ecosystem else 1
|
|
1258
1273
|
return (role_order, eco_order, d.name.lower())
|
|
1259
1274
|
|
|
1260
|
-
|
|
1275
|
+
_seen_dep_names: set[str] = set()
|
|
1276
|
+
_deduped_deps: list[Any] = []
|
|
1277
|
+
for d in sorted(direct_deps, key=_dep_sort_key):
|
|
1278
|
+
if d.name not in _seen_dep_names:
|
|
1279
|
+
_seen_dep_names.add(d.name)
|
|
1280
|
+
_deduped_deps.append(d)
|
|
1281
|
+
sm.key_dependencies = _deduped_deps[:15]
|
|
1261
1282
|
|
|
1262
1283
|
# LQN-02: deterministic NL summary
|
|
1263
1284
|
sm.project_summary = ProjectSummarizer(target).generate(sm)
|
|
@@ -1356,30 +1377,58 @@ def main(
|
|
|
1356
1377
|
_is_contract_mode = mode in ("contract", "standard")
|
|
1357
1378
|
if _is_contract_mode:
|
|
1358
1379
|
from sourcecode.contract_pipeline import ContractPipeline
|
|
1380
|
+
from sourcecode.contract_model import ContractSummary as _ContractSummary
|
|
1359
1381
|
_cp = ContractPipeline()
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1382
|
+
try:
|
|
1383
|
+
_contracts, _contract_summary = _cp.run(
|
|
1384
|
+
target,
|
|
1385
|
+
sm.file_paths,
|
|
1386
|
+
entry_points=sm.entry_points,
|
|
1387
|
+
monorepo_packages=sm.monorepo_packages,
|
|
1388
|
+
mode=mode,
|
|
1389
|
+
rank_by=rank_by, # type: ignore[arg-type]
|
|
1390
|
+
max_symbols=max_symbols,
|
|
1391
|
+
dependency_depth=dependency_depth,
|
|
1392
|
+
entrypoints_only=entrypoints_only,
|
|
1393
|
+
changed_only=changed_only,
|
|
1394
|
+
symbol=symbol,
|
|
1395
|
+
compress_types=compress_types,
|
|
1396
|
+
max_importers=max_importers,
|
|
1397
|
+
semantic_calls=sm.semantic_calls or None,
|
|
1398
|
+
code_notes=sm.code_notes or None,
|
|
1399
|
+
)
|
|
1400
|
+
except Exception as _exc:
|
|
1401
|
+
typer.echo(f"[error] contract pipeline failed: {_exc}", err=True)
|
|
1402
|
+
_contracts = []
|
|
1403
|
+
_contract_summary = _ContractSummary(
|
|
1404
|
+
mode=mode,
|
|
1405
|
+
total_files=0,
|
|
1406
|
+
extracted_files=0,
|
|
1407
|
+
filtered_files=0,
|
|
1408
|
+
method_breakdown={},
|
|
1409
|
+
ranked_by=rank_by,
|
|
1410
|
+
limitations=[f"pipeline_error: {type(_exc).__name__}"],
|
|
1411
|
+
)
|
|
1375
1412
|
sm = _replace(sm, file_contracts=_contracts, contract_summary=_contract_summary)
|
|
1376
1413
|
if symbol is not None and len(_contracts) == 0:
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1414
|
+
_jvm_stacks = {"java", "kotlin", "scala", "groovy"}
|
|
1415
|
+
_is_jvm_repo = any(s.stack in _jvm_stacks for s in sm.stacks)
|
|
1416
|
+
if _is_jvm_repo:
|
|
1417
|
+
typer.echo(
|
|
1418
|
+
f"[warning] --symbol '{symbol}' matched 0 files. "
|
|
1419
|
+
"Per-file AST extraction is not available for Java/JVM repos — "
|
|
1420
|
+
"symbol search works only with Python, TypeScript, and JavaScript. "
|
|
1421
|
+
"Use --git-context or --code-notes for JVM navigation.",
|
|
1422
|
+
err=True,
|
|
1423
|
+
)
|
|
1424
|
+
else:
|
|
1425
|
+
typer.echo(
|
|
1426
|
+
f"[warning] --symbol '{symbol}' matched 0 files. "
|
|
1427
|
+
"The symbol may not exist, the name may differ in case, "
|
|
1428
|
+
"or the file may be outside the scanned depth. "
|
|
1429
|
+
"Try --depth 8 or verify the symbol name.",
|
|
1430
|
+
err=True,
|
|
1431
|
+
)
|
|
1383
1432
|
if agent:
|
|
1384
1433
|
typer.echo(f"[contract] {len(_contracts)} files extracted ({_contract_summary.method_breakdown})", err=True)
|
|
1385
1434
|
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
"""context_scorer.py — Unified node scoring and minimum-sufficient subgraph selection.
|
|
2
|
+
|
|
3
|
+
Aggregates all available signals (structural, semantic, git, annotations, proximity)
|
|
4
|
+
into a NodeScore per file, then uses greedy selection to produce the minimum-sufficient
|
|
5
|
+
subgraph that maximises explanatory value within a context budget.
|
|
6
|
+
|
|
7
|
+
Design invariants:
|
|
8
|
+
- Deterministic: sort key is always (-score, path). Path breaks all ties.
|
|
9
|
+
- No LLMs, no randomness, no external I/O.
|
|
10
|
+
- All signals optional: degrades gracefully when data is absent.
|
|
11
|
+
- SCORER_VERSION: bump on any formula change so callers can detect drift.
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from collections import Counter, deque
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any, Optional
|
|
19
|
+
|
|
20
|
+
SCORER_VERSION = "1"
|
|
21
|
+
|
|
22
|
+
# ---------------------------------------------------------------------------
|
|
23
|
+
# Edge weight tables
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
_EDGE_BASE_WEIGHTS: dict[str, float] = {
|
|
27
|
+
"imports": 1.00, # structural dependency — strongest signal
|
|
28
|
+
"extends": 0.90, # inheritance / implementation — tight coupling
|
|
29
|
+
"calls": 0.80, # behavioral dependency
|
|
30
|
+
"contains": 0.30, # membership — low marginal information
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
_CONFIDENCE_MULT: dict[str, float] = {
|
|
34
|
+
"high": 1.0,
|
|
35
|
+
"medium": 0.7,
|
|
36
|
+
"low": 0.3,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# Annotation kinds weighted at 2× (actionable defects vs informational notes)
|
|
40
|
+
_HIGH_SEVERITY_NOTES: frozenset[str] = frozenset({"BUG", "FIXME", "HACK", "XXX"})
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# ---------------------------------------------------------------------------
|
|
44
|
+
# Data model
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class NodeScore:
|
|
49
|
+
"""Unified scoring breakdown for a single file node.
|
|
50
|
+
|
|
51
|
+
score / display_score drive all ranking and selection decisions.
|
|
52
|
+
The component fields (structural, semantic, annotation, proximity) allow
|
|
53
|
+
callers to inspect which signals dominated the final score.
|
|
54
|
+
"""
|
|
55
|
+
path: str
|
|
56
|
+
score: float # final weighted score (higher = more relevant)
|
|
57
|
+
display_score: float # clamped [0.0, 1.0] for output fields
|
|
58
|
+
structural: float # contribution from RankingEngine
|
|
59
|
+
semantic: float # call graph centrality [0.0, 1.0]
|
|
60
|
+
annotation: float # code note density [0.0, 1.0]
|
|
61
|
+
proximity: float # BFS closeness to focus [0.0, 1.0]
|
|
62
|
+
reasons: list[str]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# ---------------------------------------------------------------------------
|
|
66
|
+
# Core scorer
|
|
67
|
+
# ---------------------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
class ContextScorer:
|
|
70
|
+
"""Unified file scoring and minimum-sufficient subgraph selection.
|
|
71
|
+
|
|
72
|
+
Stateless once constructed. Thread-safe (no mutable state after __init__).
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
def __init__(
|
|
76
|
+
self,
|
|
77
|
+
monorepo_packages: Optional[list] = None,
|
|
78
|
+
) -> None:
|
|
79
|
+
from sourcecode.ranking_engine import RankingEngine
|
|
80
|
+
self._engine = RankingEngine(monorepo_packages or [])
|
|
81
|
+
|
|
82
|
+
def score_nodes(
|
|
83
|
+
self,
|
|
84
|
+
contracts: list[Any],
|
|
85
|
+
*,
|
|
86
|
+
semantic_calls: Optional[list] = None,
|
|
87
|
+
git_hotspots: Optional[dict[str, int]] = None,
|
|
88
|
+
code_notes: Optional[list] = None,
|
|
89
|
+
focus_path: Optional[str] = None,
|
|
90
|
+
task: str = "default",
|
|
91
|
+
) -> dict[str, NodeScore]:
|
|
92
|
+
"""Compute a NodeScore for every contract.
|
|
93
|
+
|
|
94
|
+
Parameters
|
|
95
|
+
----------
|
|
96
|
+
contracts FileContract list. fan_in, fan_out, is_entrypoint,
|
|
97
|
+
is_changed, and exports must be set before calling.
|
|
98
|
+
semantic_calls list[CallRecord] from --semantics (optional).
|
|
99
|
+
git_hotspots {path: commit_count} from git analysis (optional).
|
|
100
|
+
code_notes list[CodeNote] from --code-notes (optional).
|
|
101
|
+
focus_path Anchor file for proximity BFS (optional).
|
|
102
|
+
task Task profile: fix-bug | refactor | explain | …
|
|
103
|
+
|
|
104
|
+
Returns
|
|
105
|
+
-------
|
|
106
|
+
dict mapping path → NodeScore for every contract path.
|
|
107
|
+
"""
|
|
108
|
+
from sourcecode.ranking_engine import TASK_WEIGHTS
|
|
109
|
+
|
|
110
|
+
w = TASK_WEIGHTS.get(task, TASK_WEIGHTS["default"])
|
|
111
|
+
_hotspots = git_hotspots or {}
|
|
112
|
+
max_fan_in = max((c.fan_in for c in contracts), default=1)
|
|
113
|
+
max_churn = max(_hotspots.values(), default=1)
|
|
114
|
+
|
|
115
|
+
# Pre-compute optional signal maps
|
|
116
|
+
sem_centrality: dict[str, float] = {}
|
|
117
|
+
if semantic_calls:
|
|
118
|
+
sem_centrality = _semantic_centrality(semantic_calls, contracts)
|
|
119
|
+
max_semantic = max(sem_centrality.values(), default=1.0) or 1.0
|
|
120
|
+
|
|
121
|
+
ann_density: dict[str, float] = {}
|
|
122
|
+
if code_notes:
|
|
123
|
+
ann_density = _annotation_density(code_notes, contracts)
|
|
124
|
+
|
|
125
|
+
prox_scores: dict[str, float] = {}
|
|
126
|
+
if focus_path:
|
|
127
|
+
prox_scores = _proximity_bfs(focus_path, contracts, semantic_calls or [])
|
|
128
|
+
|
|
129
|
+
result: dict[str, NodeScore] = {}
|
|
130
|
+
for c in contracts:
|
|
131
|
+
sem = sem_centrality.get(c.path, 0.0)
|
|
132
|
+
ann = ann_density.get(c.path, 0.0)
|
|
133
|
+
prox = prox_scores.get(c.path, 0.0)
|
|
134
|
+
|
|
135
|
+
# Structural + git + annotation + semantic centrality via unified engine
|
|
136
|
+
fs = self._engine.score(
|
|
137
|
+
c.path,
|
|
138
|
+
fan_in=c.fan_in,
|
|
139
|
+
fan_out=c.fan_out,
|
|
140
|
+
max_fan_in=max_fan_in,
|
|
141
|
+
git_churn=_hotspots.get(c.path, 0),
|
|
142
|
+
max_churn=max_churn,
|
|
143
|
+
is_entrypoint=c.is_entrypoint,
|
|
144
|
+
is_changed=c.is_changed,
|
|
145
|
+
export_count=len(c.exports),
|
|
146
|
+
task=task,
|
|
147
|
+
semantic_centrality=sem,
|
|
148
|
+
max_semantic=max_semantic,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Proximity is a graph operation, computed here and added on top
|
|
152
|
+
prox_contrib = prox * 0.50 * w.proximity
|
|
153
|
+
|
|
154
|
+
final = fs.score + prox_contrib
|
|
155
|
+
|
|
156
|
+
reasons = list(fs.reasons)
|
|
157
|
+
if prox >= 0.80 and prox_contrib > 0:
|
|
158
|
+
reasons.append("close to focus")
|
|
159
|
+
elif prox >= 0.50 and prox_contrib > 0:
|
|
160
|
+
reasons.append("near focus")
|
|
161
|
+
|
|
162
|
+
result[c.path] = NodeScore(
|
|
163
|
+
path=c.path,
|
|
164
|
+
score=final,
|
|
165
|
+
display_score=max(0.0, min(1.0, final)),
|
|
166
|
+
structural=fs.score,
|
|
167
|
+
semantic=sem,
|
|
168
|
+
annotation=ann,
|
|
169
|
+
proximity=prox,
|
|
170
|
+
reasons=reasons,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
return result
|
|
174
|
+
|
|
175
|
+
def select_subgraph(
|
|
176
|
+
self,
|
|
177
|
+
node_scores: dict[str, NodeScore],
|
|
178
|
+
contracts: list[Any],
|
|
179
|
+
*,
|
|
180
|
+
budget: int = 30,
|
|
181
|
+
min_score: float = 0.05,
|
|
182
|
+
) -> list[str]:
|
|
183
|
+
"""Greedy minimum-sufficient subgraph selection with diversity re-ranking.
|
|
184
|
+
|
|
185
|
+
At each round, recomputes effective scores for all remaining candidates
|
|
186
|
+
(raw_score × (1 - redundancy_penalty)), then picks the highest. This
|
|
187
|
+
allows a file from a new directory to beat a clustered sibling even if
|
|
188
|
+
the sibling has a higher raw score — the selection actively prefers
|
|
189
|
+
coverage over concentration.
|
|
190
|
+
|
|
191
|
+
Stops when the budget is exhausted or no remaining candidate has an
|
|
192
|
+
effective score above min_score.
|
|
193
|
+
|
|
194
|
+
O(n × budget) — negligible for typical budgets (15-30) and file counts.
|
|
195
|
+
Deterministic: tie-break by path on every round.
|
|
196
|
+
|
|
197
|
+
Parameters
|
|
198
|
+
----------
|
|
199
|
+
node_scores output of score_nodes()
|
|
200
|
+
contracts same FileContract list passed to score_nodes()
|
|
201
|
+
(used for directory-based redundancy; may be empty)
|
|
202
|
+
budget maximum number of nodes to select
|
|
203
|
+
min_score discard candidates whose effective score is below this
|
|
204
|
+
"""
|
|
205
|
+
contract_map = {c.path: c for c in contracts}
|
|
206
|
+
remaining: dict[str, NodeScore] = dict(node_scores)
|
|
207
|
+
selected: list[str] = []
|
|
208
|
+
selected_set: set[str] = set()
|
|
209
|
+
|
|
210
|
+
while len(selected) < budget and remaining:
|
|
211
|
+
best_path: str | None = None
|
|
212
|
+
best_effective: float = -1.0
|
|
213
|
+
|
|
214
|
+
for path, ns in remaining.items():
|
|
215
|
+
if ns.score < min_score:
|
|
216
|
+
continue
|
|
217
|
+
penalty = _redundancy_penalty(path, selected_set, contract_map)
|
|
218
|
+
effective = ns.score * (1.0 - penalty)
|
|
219
|
+
# Strict tie-break by path ensures determinism
|
|
220
|
+
if effective > best_effective or (
|
|
221
|
+
effective == best_effective
|
|
222
|
+
and best_path is not None
|
|
223
|
+
and path < best_path
|
|
224
|
+
):
|
|
225
|
+
best_effective = effective
|
|
226
|
+
best_path = path
|
|
227
|
+
|
|
228
|
+
if best_path is None or best_effective < min_score:
|
|
229
|
+
break
|
|
230
|
+
|
|
231
|
+
selected.append(best_path)
|
|
232
|
+
selected_set.add(best_path)
|
|
233
|
+
del remaining[best_path]
|
|
234
|
+
|
|
235
|
+
return selected
|
|
236
|
+
|
|
237
|
+
@staticmethod
|
|
238
|
+
def edge_weight(kind: str, confidence: str) -> float:
|
|
239
|
+
"""Scalar weight for a graph edge based on relationship type and confidence.
|
|
240
|
+
|
|
241
|
+
Higher weight = stronger information dependency between the connected nodes.
|
|
242
|
+
"""
|
|
243
|
+
base = _EDGE_BASE_WEIGHTS.get(kind, 0.50)
|
|
244
|
+
mult = _CONFIDENCE_MULT.get(confidence, 0.50)
|
|
245
|
+
return base * mult
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
# ---------------------------------------------------------------------------
|
|
249
|
+
# Signal computers (module-level, pure functions)
|
|
250
|
+
# ---------------------------------------------------------------------------
|
|
251
|
+
|
|
252
|
+
def _semantic_centrality(
|
|
253
|
+
semantic_calls: list,
|
|
254
|
+
contracts: list,
|
|
255
|
+
) -> dict[str, float]:
|
|
256
|
+
"""Per-file centrality from the call graph.
|
|
257
|
+
|
|
258
|
+
centrality(path) = (weighted_fan_in × 2 + weighted_fan_out) / max
|
|
259
|
+
where weight = confidence multiplier (high=1.0, medium=0.7, low=0.3).
|
|
260
|
+
|
|
261
|
+
Returns a dict normalised to [0.0, 1.0] across the contract set.
|
|
262
|
+
"""
|
|
263
|
+
path_set = {c.path for c in contracts}
|
|
264
|
+
fan_in: Counter[str] = Counter()
|
|
265
|
+
fan_out: Counter[str] = Counter()
|
|
266
|
+
|
|
267
|
+
for call in semantic_calls:
|
|
268
|
+
w = _CONFIDENCE_MULT.get(getattr(call, "confidence", "medium"), 0.7)
|
|
269
|
+
callee = getattr(call, "callee_path", None)
|
|
270
|
+
caller = getattr(call, "caller_path", None)
|
|
271
|
+
if callee and callee in path_set:
|
|
272
|
+
fan_in[callee] += w
|
|
273
|
+
if caller and caller in path_set:
|
|
274
|
+
fan_out[caller] += w
|
|
275
|
+
|
|
276
|
+
raw = {p: fan_in[p] * 2.0 + fan_out[p] for p in path_set}
|
|
277
|
+
max_val = max(raw.values(), default=0.0)
|
|
278
|
+
if max_val <= 0.0:
|
|
279
|
+
return {p: 0.0 for p in path_set}
|
|
280
|
+
return {p: v / max_val for p, v in raw.items()}
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def _proximity_bfs(
|
|
284
|
+
focus_path: str,
|
|
285
|
+
contracts: list,
|
|
286
|
+
semantic_calls: list,
|
|
287
|
+
) -> dict[str, float]:
|
|
288
|
+
"""BFS from focus_path through import + call edges.
|
|
289
|
+
|
|
290
|
+
Traversal is bidirectional (imports and calls traversed in both directions)
|
|
291
|
+
so the proximity score reflects reachability in any direction from the focus.
|
|
292
|
+
|
|
293
|
+
proximity(path) = 1.0 / (2 ** distance)
|
|
294
|
+
distance=0 → 1.00 (the focus itself)
|
|
295
|
+
distance=1 → 0.50
|
|
296
|
+
distance=2 → 0.25
|
|
297
|
+
distance=3 → 0.125
|
|
298
|
+
distance=4 → 0.0625 (max depth)
|
|
299
|
+
|
|
300
|
+
BFS neighbours are sorted before enqueuing to ensure determinism.
|
|
301
|
+
"""
|
|
302
|
+
path_set = {c.path for c in contracts}
|
|
303
|
+
|
|
304
|
+
# Build bidirectional adjacency from import graph
|
|
305
|
+
adj: dict[str, set[str]] = {p: set() for p in path_set}
|
|
306
|
+
for c in contracts:
|
|
307
|
+
base_dir = str(Path(c.path).parent).replace("\\", "/")
|
|
308
|
+
for imp in c.imports:
|
|
309
|
+
src = getattr(imp, "source", "")
|
|
310
|
+
if not src.startswith("."):
|
|
311
|
+
continue
|
|
312
|
+
for t in _resolve_import(base_dir, src, path_set):
|
|
313
|
+
adj[c.path].add(t)
|
|
314
|
+
adj[t].add(c.path)
|
|
315
|
+
|
|
316
|
+
# Augment with call graph edges
|
|
317
|
+
for call in semantic_calls:
|
|
318
|
+
caller = getattr(call, "caller_path", None)
|
|
319
|
+
callee = getattr(call, "callee_path", None)
|
|
320
|
+
if caller in adj and callee in adj:
|
|
321
|
+
adj[caller].add(callee)
|
|
322
|
+
adj[callee].add(caller)
|
|
323
|
+
|
|
324
|
+
if focus_path not in adj:
|
|
325
|
+
return {}
|
|
326
|
+
|
|
327
|
+
distances: dict[str, int] = {focus_path: 0}
|
|
328
|
+
queue: deque[str] = deque([focus_path])
|
|
329
|
+
while queue:
|
|
330
|
+
node = queue.popleft()
|
|
331
|
+
d = distances[node]
|
|
332
|
+
if d >= 4:
|
|
333
|
+
continue
|
|
334
|
+
for neighbor in sorted(adj.get(node, set())):
|
|
335
|
+
if neighbor not in distances:
|
|
336
|
+
distances[neighbor] = d + 1
|
|
337
|
+
queue.append(neighbor)
|
|
338
|
+
|
|
339
|
+
return {p: 1.0 / (2 ** d) for p, d in distances.items()}
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def _annotation_density(
|
|
343
|
+
code_notes: list,
|
|
344
|
+
contracts: list,
|
|
345
|
+
) -> dict[str, float]:
|
|
346
|
+
"""Severity-weighted annotation density per file, normalised [0.0, 1.0].
|
|
347
|
+
|
|
348
|
+
BUG / FIXME / HACK / XXX count 2×; all other kinds count 1×.
|
|
349
|
+
"""
|
|
350
|
+
path_set = {c.path for c in contracts}
|
|
351
|
+
weighted: Counter[str] = Counter()
|
|
352
|
+
for note in code_notes:
|
|
353
|
+
path = getattr(note, "path", None)
|
|
354
|
+
if path not in path_set:
|
|
355
|
+
continue
|
|
356
|
+
kind = getattr(note, "kind", "").upper()
|
|
357
|
+
weighted[path] += 2.0 if kind in _HIGH_SEVERITY_NOTES else 1.0
|
|
358
|
+
|
|
359
|
+
max_val = max(weighted.values(), default=1.0)
|
|
360
|
+
return {p: min(weighted.get(p, 0.0) / max_val, 1.0) for p in path_set}
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def _redundancy_penalty(
|
|
364
|
+
path: str,
|
|
365
|
+
selected_set: set[str],
|
|
366
|
+
contract_map: dict,
|
|
367
|
+
) -> float:
|
|
368
|
+
"""Penalty for adding a file from the same directory as already-selected files.
|
|
369
|
+
|
|
370
|
+
Rationale: files in the same directory address the same concern; the
|
|
371
|
+
marginal explanatory gain of the n-th file from a directory is lower than
|
|
372
|
+
that of the first file from a new directory.
|
|
373
|
+
|
|
374
|
+
Penalty grows by 0.10 per same-directory sibling, capped at 0.40.
|
|
375
|
+
The 0.40 cap ensures no node is ever fully excluded by proximity alone.
|
|
376
|
+
"""
|
|
377
|
+
if not selected_set:
|
|
378
|
+
return 0.0
|
|
379
|
+
path_dir = str(Path(path).parent)
|
|
380
|
+
same_dir_count = sum(
|
|
381
|
+
1 for s in selected_set
|
|
382
|
+
if str(Path(s).parent) == path_dir
|
|
383
|
+
)
|
|
384
|
+
return min(same_dir_count * 0.10, 0.40)
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def _resolve_import(base_dir: str, src: str, path_set: set[str]) -> list[str]:
|
|
388
|
+
"""Approximate resolution of a relative import specifier to known paths.
|
|
389
|
+
|
|
390
|
+
Mirrors the logic in contract_pipeline._resolve_relative without importing
|
|
391
|
+
from that module (avoids circular import).
|
|
392
|
+
"""
|
|
393
|
+
src = src.lstrip("./")
|
|
394
|
+
if not src:
|
|
395
|
+
return []
|
|
396
|
+
exts = (".ts", ".tsx", ".js", ".jsx", ".py", "/index.ts", "/index.js", "/index.tsx")
|
|
397
|
+
for ext in exts:
|
|
398
|
+
candidate = f"{base_dir}/{src}{ext}".replace("//", "/")
|
|
399
|
+
if candidate in path_set:
|
|
400
|
+
return [candidate]
|
|
401
|
+
candidate = f"{base_dir}/{src}".replace("//", "/")
|
|
402
|
+
if candidate in path_set:
|
|
403
|
+
return [candidate]
|
|
404
|
+
return []
|
sourcecode/contract_pipeline.py
CHANGED
|
@@ -176,6 +176,8 @@ class ContractPipeline:
|
|
|
176
176
|
symbol: Optional[str] = None,
|
|
177
177
|
compress_types: bool = False,
|
|
178
178
|
max_importers: int = 50,
|
|
179
|
+
semantic_calls: Optional[list] = None,
|
|
180
|
+
code_notes: Optional[list] = None,
|
|
179
181
|
) -> tuple[list[FileContract], ContractSummary]:
|
|
180
182
|
"""Run the full extraction pipeline.
|
|
181
183
|
|
|
@@ -239,7 +241,9 @@ class ContractPipeline:
|
|
|
239
241
|
contracts.append(contract)
|
|
240
242
|
method_counts[contract.extraction_method] += 1
|
|
241
243
|
|
|
242
|
-
|
|
244
|
+
_js_ts_languages = {"typescript", "javascript", "tsx", "jsx"}
|
|
245
|
+
_has_js_ts = any(c.language in _js_ts_languages for c in contracts)
|
|
246
|
+
if _has_js_ts and not self._extractor.has_tree_sitter():
|
|
243
247
|
limitations.append(
|
|
244
248
|
"tree_sitter_unavailable: JS/TS extraction uses heuristics. "
|
|
245
249
|
"Install with: pip install 'sourcecode[ast]'"
|
|
@@ -257,24 +261,24 @@ class ContractPipeline:
|
|
|
257
261
|
if rank_by == "git-churn":
|
|
258
262
|
churn = _get_git_churn(root, [c.path for c in contracts])
|
|
259
263
|
|
|
260
|
-
# 6. Compute relevance scores via unified
|
|
261
|
-
|
|
262
|
-
|
|
264
|
+
# 6. Compute relevance scores via unified scoring engine.
|
|
265
|
+
# ContextScorer wraps RankingEngine and enriches scores with semantic
|
|
266
|
+
# centrality (when semantic_calls available) and annotation density
|
|
267
|
+
# (when code_notes available). Falls back to structural signals only
|
|
268
|
+
# when neither is present — identical to the old behaviour.
|
|
269
|
+
from sourcecode.context_scorer import ContextScorer
|
|
270
|
+
_ctx_scorer = ContextScorer(monorepo_packages)
|
|
271
|
+
_node_scores = _ctx_scorer.score_nodes(
|
|
272
|
+
contracts,
|
|
273
|
+
semantic_calls=semantic_calls,
|
|
274
|
+
code_notes=code_notes,
|
|
275
|
+
git_hotspots=churn,
|
|
276
|
+
task="default",
|
|
277
|
+
)
|
|
263
278
|
for c in contracts:
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
fan_out=c.fan_out,
|
|
268
|
-
max_fan_in=max_fan_in,
|
|
269
|
-
git_churn=churn.get(c.path, 0),
|
|
270
|
-
max_churn=max_churn_val,
|
|
271
|
-
is_entrypoint=c.is_entrypoint,
|
|
272
|
-
is_changed=c.is_changed,
|
|
273
|
-
export_count=len(c.exports),
|
|
274
|
-
task="default",
|
|
275
|
-
)
|
|
276
|
-
c.relevance_score = fs.display_score
|
|
277
|
-
c.ranking_reasons = fs.reasons
|
|
279
|
+
ns = _node_scores[c.path]
|
|
280
|
+
c.relevance_score = ns.display_score
|
|
281
|
+
c.ranking_reasons = ns.reasons
|
|
278
282
|
|
|
279
283
|
# 7. Rank
|
|
280
284
|
contracts = self._rank(contracts, rank_by)
|
|
@@ -66,6 +66,13 @@ class HeuristicDetector(AbstractDetector):
|
|
|
66
66
|
counts[stack] += 1
|
|
67
67
|
break
|
|
68
68
|
|
|
69
|
+
# Suppress minority stacks: if a language appears in fewer than 3 files
|
|
70
|
+
# AND represents less than 10% of detected source files, it is likely
|
|
71
|
+
# noise (stray config files, vendored snippets) rather than a real stack.
|
|
72
|
+
# Always emit the dominant language regardless of absolute count.
|
|
73
|
+
total_detected = sum(counts.values())
|
|
74
|
+
_ABS_MIN = 3
|
|
75
|
+
_REL_MIN = 0.10
|
|
69
76
|
stacks = [
|
|
70
77
|
StackDetection(
|
|
71
78
|
stack=stack,
|
|
@@ -73,7 +80,8 @@ class HeuristicDetector(AbstractDetector):
|
|
|
73
80
|
confidence="low",
|
|
74
81
|
manifests=[],
|
|
75
82
|
)
|
|
76
|
-
for stack,
|
|
83
|
+
for stack, count in counts.most_common()
|
|
84
|
+
if count >= _ABS_MIN or (total_detected > 0 and count / total_detected >= _REL_MIN)
|
|
77
85
|
]
|
|
78
86
|
|
|
79
87
|
entry_points: list[EntryPoint] = []
|
sourcecode/env_analyzer.py
CHANGED
|
@@ -11,8 +11,8 @@ _MAX_FILE_SIZE = 512 * 1024 # 512 KB
|
|
|
11
11
|
|
|
12
12
|
_SKIP_DIRS = {
|
|
13
13
|
"node_modules", ".git", "__pycache__", ".venv", "venv",
|
|
14
|
-
".mypy_cache", "dist", "build", "
|
|
15
|
-
".next", ".nuxt", ".output", "vendor",
|
|
14
|
+
".mypy_cache", "dist", "build", "target", ".gradle",
|
|
15
|
+
".tox", ".eggs", "coverage", ".next", ".nuxt", ".output", "vendor",
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
_CODE_EXTENSIONS = {
|
sourcecode/prepare_context.py
CHANGED
|
@@ -701,7 +701,33 @@ class TaskContextBuilder:
|
|
|
701
701
|
|
|
702
702
|
# Deterministic: score desc, then path asc as tiebreaker
|
|
703
703
|
scored.sort(key=lambda x: (-x[0], x[1]))
|
|
704
|
-
|
|
704
|
+
|
|
705
|
+
# Apply directory-diversity selection via ContextScorer.
|
|
706
|
+
# Files from the same directory share the same concern; the scorer
|
|
707
|
+
# applies a small redundancy penalty so the final set spans more of
|
|
708
|
+
# the codebase rather than clustering inside a single directory.
|
|
709
|
+
# Falls back to top-15 slice when scorer is unavailable.
|
|
710
|
+
try:
|
|
711
|
+
from sourcecode.context_scorer import ContextScorer, NodeScore
|
|
712
|
+
_ctx = ContextScorer()
|
|
713
|
+
_ns: dict[str, NodeScore] = {
|
|
714
|
+
path: NodeScore(
|
|
715
|
+
path=path,
|
|
716
|
+
score=total,
|
|
717
|
+
display_score=min(total / 3.0, 1.0),
|
|
718
|
+
structural=total,
|
|
719
|
+
semantic=0.0,
|
|
720
|
+
annotation=0.0,
|
|
721
|
+
proximity=0.0,
|
|
722
|
+
reasons=[rf.reason] if rf.reason else ["source file"],
|
|
723
|
+
)
|
|
724
|
+
for total, path, rf in scored
|
|
725
|
+
}
|
|
726
|
+
_selected = _ctx.select_subgraph(_ns, contracts=[], budget=15, min_score=0.05)
|
|
727
|
+
_rf_map = {path: rf for _, path, rf in scored}
|
|
728
|
+
return [_rf_map[p] for p in _selected if p in _rf_map]
|
|
729
|
+
except Exception:
|
|
730
|
+
return [f for _, _, f in scored[:15]]
|
|
705
731
|
|
|
706
732
|
def _is_test(self, path: str) -> bool:
|
|
707
733
|
name = Path(path).name.lower()
|
sourcecode/ranking_engine.py
CHANGED
|
@@ -45,60 +45,71 @@ class TaskWeights:
|
|
|
45
45
|
code_notes: float = 0.5
|
|
46
46
|
exports: float = 0.3
|
|
47
47
|
is_changed: float = 0.8
|
|
48
|
+
# Call graph centrality from --semantics (ContextScorer feeds this in)
|
|
49
|
+
semantic_centrality: float = 0.5
|
|
50
|
+
# BFS proximity to a focus symbol/file (added by ContextScorer on top)
|
|
51
|
+
proximity: float = 1.0
|
|
48
52
|
|
|
49
53
|
|
|
50
54
|
# Task profiles: each emphasizes different signals for different agent goals.
|
|
51
55
|
# The contrast between profiles is intentional — fix-bug and explain must
|
|
52
56
|
# produce meaningfully different ranked sets from the same codebase.
|
|
53
57
|
TASK_WEIGHTS: dict[str, TaskWeights] = {
|
|
54
|
-
# fix-bug:
|
|
58
|
+
# fix-bug: bug annotations, recent churn, changed files, proximity to focus
|
|
55
59
|
"fix-bug": TaskWeights(
|
|
56
60
|
path_relevance=0.5, entrypoint=0.5,
|
|
57
61
|
fan_in=0.8, fan_out=0.3,
|
|
58
62
|
git_churn=1.5, code_notes=3.0,
|
|
59
63
|
exports=0.2, is_changed=2.0,
|
|
64
|
+
semantic_centrality=0.5, proximity=2.0,
|
|
60
65
|
),
|
|
61
|
-
# refactor:
|
|
66
|
+
# refactor: hub modules, coupling, technical debt, call graph hubs
|
|
62
67
|
"refactor": TaskWeights(
|
|
63
68
|
path_relevance=0.8, entrypoint=0.3,
|
|
64
69
|
fan_in=2.0, fan_out=2.0,
|
|
65
70
|
git_churn=0.3, code_notes=2.0,
|
|
66
71
|
exports=1.0, is_changed=0.3,
|
|
72
|
+
semantic_centrality=1.5, proximity=0.5,
|
|
67
73
|
),
|
|
68
|
-
# explain: stable core, entrypoints,
|
|
74
|
+
# explain: stable core, entrypoints, call graph backbone — ignore churn
|
|
69
75
|
"explain": TaskWeights(
|
|
70
76
|
path_relevance=2.0, entrypoint=3.0,
|
|
71
77
|
fan_in=0.8, fan_out=0.3,
|
|
72
78
|
git_churn=0.0, code_notes=0.0,
|
|
73
79
|
exports=0.5, is_changed=0.0,
|
|
80
|
+
semantic_centrality=1.0, proximity=0.3,
|
|
74
81
|
),
|
|
75
|
-
# onboard:
|
|
82
|
+
# onboard: entrypoints + hub modules + call graph backbone
|
|
76
83
|
"onboard": TaskWeights(
|
|
77
84
|
path_relevance=2.0, entrypoint=3.0,
|
|
78
85
|
fan_in=1.2, fan_out=0.5,
|
|
79
86
|
git_churn=0.0, code_notes=0.0,
|
|
80
87
|
exports=1.0, is_changed=0.0,
|
|
88
|
+
semantic_centrality=1.2, proximity=0.3,
|
|
81
89
|
),
|
|
82
|
-
# generate-tests:
|
|
90
|
+
# generate-tests: large public API, call graph reachability
|
|
83
91
|
"generate-tests": TaskWeights(
|
|
84
92
|
path_relevance=0.8, entrypoint=0.3,
|
|
85
93
|
fan_in=1.5, fan_out=0.8,
|
|
86
94
|
git_churn=0.5, code_notes=0.5,
|
|
87
95
|
exports=2.5, is_changed=0.5,
|
|
96
|
+
semantic_centrality=0.8, proximity=0.5,
|
|
88
97
|
),
|
|
89
|
-
# review-pr: changed files
|
|
98
|
+
# review-pr: changed files, their importers, impact radius
|
|
90
99
|
"review-pr": TaskWeights(
|
|
91
100
|
path_relevance=0.5, entrypoint=0.5,
|
|
92
101
|
fan_in=1.5, fan_out=0.5,
|
|
93
102
|
git_churn=0.5, code_notes=0.8,
|
|
94
103
|
exports=0.3, is_changed=3.0,
|
|
104
|
+
semantic_centrality=1.0, proximity=1.5,
|
|
95
105
|
),
|
|
96
|
-
# delta: changed files
|
|
106
|
+
# delta: changed files, dependency impact, call graph proximity
|
|
97
107
|
"delta": TaskWeights(
|
|
98
108
|
path_relevance=0.5, entrypoint=0.5,
|
|
99
109
|
fan_in=1.5, fan_out=0.5,
|
|
100
110
|
git_churn=0.5, code_notes=0.5,
|
|
101
111
|
exports=0.3, is_changed=3.0,
|
|
112
|
+
semantic_centrality=1.0, proximity=1.0,
|
|
102
113
|
),
|
|
103
114
|
# default: balanced, no task bias
|
|
104
115
|
"default": TaskWeights(),
|
|
@@ -139,6 +150,8 @@ class RankingEngine:
|
|
|
139
150
|
code_note_count: int = 0,
|
|
140
151
|
export_count: int = 0,
|
|
141
152
|
task: str = "default",
|
|
153
|
+
semantic_centrality: float = 0.0,
|
|
154
|
+
max_semantic: float = 1.0,
|
|
142
155
|
) -> FileScore:
|
|
143
156
|
"""Compute a scored, explained ranking for a single file.
|
|
144
157
|
|
|
@@ -203,6 +216,15 @@ class RankingEngine:
|
|
|
203
216
|
raw += 0.2 * w.is_changed
|
|
204
217
|
reasons.append("uncommitted changes")
|
|
205
218
|
|
|
219
|
+
# 9. Semantic call-graph centrality (fed by ContextScorer from --semantics)
|
|
220
|
+
if semantic_centrality > 0 and w.semantic_centrality > 0:
|
|
221
|
+
sc_norm = min(semantic_centrality / max(max_semantic, 1e-9), 1.0)
|
|
222
|
+
raw += sc_norm * 0.25 * w.semantic_centrality
|
|
223
|
+
if sc_norm >= 0.60:
|
|
224
|
+
reasons.append("call graph hub")
|
|
225
|
+
elif sc_norm >= 0.25:
|
|
226
|
+
reasons.append("call graph contributor")
|
|
227
|
+
|
|
206
228
|
# Monorepo package role
|
|
207
229
|
pkg_role = self._scorer.package_role(norm)
|
|
208
230
|
if pkg_role in _WORKSPACE_CORE_ROLES:
|
sourcecode/serializer.py
CHANGED
|
@@ -121,10 +121,16 @@ def _dependency_groups(sm: SourceMap) -> dict[str, list[dict[str, Any]]]:
|
|
|
121
121
|
|
|
122
122
|
if role in _PRODUCTION_DEP_ROLES and scope not in {"dev"}:
|
|
123
123
|
groups["production_dependencies"].append(item)
|
|
124
|
+
_jvm_ecosystems = {"maven", "gradle", "java", "kotlin", "scala", "groovy"}
|
|
124
125
|
if dep.source == "manifest" and name_key not in import_index:
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
if dep.ecosystem in _jvm_ecosystems:
|
|
127
|
+
# Static import check unsupported for JVM: import index only covers
|
|
128
|
+
# Python/JS/TS. Flagging JVM deps as suspicious produces only false positives.
|
|
129
|
+
pass
|
|
130
|
+
else:
|
|
131
|
+
suspect = dict(item)
|
|
132
|
+
suspect["reason"] = "declared as production dependency but no static import observed"
|
|
133
|
+
groups["suspicious_dependencies"].append(suspect)
|
|
128
134
|
elif role in _TEST_DEP_ROLES:
|
|
129
135
|
groups["test_utilities"].append(item)
|
|
130
136
|
elif role in _BUILD_DEP_ROLES:
|
|
@@ -186,6 +192,21 @@ def _file_relevance(sm: SourceMap, *, limit: int = 15) -> list[dict[str, Any]]:
|
|
|
186
192
|
git_churn = {h.file: h.commit_count for h in gc.change_hotspots}
|
|
187
193
|
max_churn = max(git_churn.values(), default=1)
|
|
188
194
|
|
|
195
|
+
# Incorporate semantic hotspots from --semantics when available.
|
|
196
|
+
# Hotspots rank files by call-graph centrality (fan_in×2 + fan_out),
|
|
197
|
+
# normalised across the analysed files.
|
|
198
|
+
semantic_hub_scores: dict[str, float] = {}
|
|
199
|
+
ss = sm.semantic_summary
|
|
200
|
+
if ss and getattr(ss, "requested", False) and ss.hotspots:
|
|
201
|
+
max_importance = max(
|
|
202
|
+
(h.get("importance_score", 0.0) for h in ss.hotspots),
|
|
203
|
+
default=1.0,
|
|
204
|
+
) or 1.0
|
|
205
|
+
for h in ss.hotspots:
|
|
206
|
+
p = h.get("path", "")
|
|
207
|
+
if p:
|
|
208
|
+
semantic_hub_scores[p] = h.get("importance_score", 0.0) / max_importance
|
|
209
|
+
|
|
189
210
|
entry_paths = {ep.path for ep in sm.entry_points}
|
|
190
211
|
scored: list[tuple[float, dict[str, Any]]] = []
|
|
191
212
|
|
|
@@ -202,7 +223,9 @@ def _file_relevance(sm: SourceMap, *, limit: int = 15) -> list[dict[str, Any]]:
|
|
|
202
223
|
continue
|
|
203
224
|
|
|
204
225
|
content_rel = file_class.relevance if file_class else 0.0
|
|
205
|
-
|
|
226
|
+
# Semantic hub bonus: normalised call-graph centrality adds up to +0.30
|
|
227
|
+
sem_hub = semantic_hub_scores.get(path, 0.0) * 0.30
|
|
228
|
+
combined = fs.score + content_rel + sem_hub
|
|
206
229
|
|
|
207
230
|
if combined <= 0 and not (file_class and file_class.relevance > 0.3):
|
|
208
231
|
continue
|
|
@@ -217,6 +240,8 @@ def _file_relevance(sm: SourceMap, *, limit: int = 15) -> list[dict[str, Any]]:
|
|
|
217
240
|
}
|
|
218
241
|
|
|
219
242
|
ranking_reasons = [r for r in fs.reasons if r != "source file"]
|
|
243
|
+
if sem_hub >= 0.15:
|
|
244
|
+
ranking_reasons.append("call graph hub")
|
|
220
245
|
if ranking_reasons:
|
|
221
246
|
item["ranking_reasons"] = ranking_reasons
|
|
222
247
|
|
|
@@ -1,26 +1,27 @@
|
|
|
1
|
-
sourcecode/__init__.py,sha256=
|
|
1
|
+
sourcecode/__init__.py,sha256=GGAUffktLLZY15PHqWZ0id9C28zA6fs7XQ7oxyKmNT0,103
|
|
2
2
|
sourcecode/adaptive_scanner.py,sha256=6dh34C2qZXyRbw-8xBhbEwDdXanM6CRFRWayVoYITnA,10190
|
|
3
3
|
sourcecode/architecture_analyzer.py,sha256=O4AXc7l_WTzIXrcAzstqZy-TGKNaFa6p3MzpgVjaO8g,27749
|
|
4
4
|
sourcecode/architecture_summary.py,sha256=rSY5MRiaz4N1YdG0pqDTDuFjSN7PO_Zplx-dtNzv2Yo,19985
|
|
5
5
|
sourcecode/ast_extractor.py,sha256=0OHQwTUBBc9lmqPLryVeB1z8dGIC6NhLlar800CD9oI,41129
|
|
6
6
|
sourcecode/classifier.py,sha256=GKTMN8qKZX7ponSwDJfN08RrasI4CVpq1_gFBgEopps,7093
|
|
7
|
-
sourcecode/cli.py,sha256=
|
|
7
|
+
sourcecode/cli.py,sha256=pjBq-ZIWqoBbESWorW2d0kIGtkby9urt_38ACCKqK1Q,71026
|
|
8
8
|
sourcecode/code_notes_analyzer.py,sha256=rRd8bFYV0krjlxxQV0wenwE9K7pVpUQSR7KvSvUQKw4,9226
|
|
9
9
|
sourcecode/confidence_analyzer.py,sha256=HxJMPLI5ulqtkncnv98W4iVO6yMbpQo87VuxiuNbDmY,12167
|
|
10
|
+
sourcecode/context_scorer.py,sha256=nhppAo80fblAqcB9Ns0iQd21TZUrl2mQMo_xzPgavRE,14679
|
|
10
11
|
sourcecode/context_summarizer.py,sha256=CiQrfBEzun949bWvmLabWoj2HhPn6Lw62ofqnsy0FlQ,6503
|
|
11
12
|
sourcecode/contract_model.py,sha256=gCf9-Kj0G7l0lvRTAcRfFAfMgs1Rpizv4mKovQLYUkw,3434
|
|
12
|
-
sourcecode/contract_pipeline.py,sha256=
|
|
13
|
+
sourcecode/contract_pipeline.py,sha256=wWkm0_zScUy-Q-mrAG57qysYxjNPOybjObxpwp859e0,24615
|
|
13
14
|
sourcecode/coverage_parser.py,sha256=q0LeZJaX1bnntLu-ImksdBsMlpsVmk_iUfSaB4eaJGo,19702
|
|
14
15
|
sourcecode/dependency_analyzer.py,sha256=Exq0BfInvfS5iAg9xAr6WI2uPNuotkIudTKcYJcRhB8,52757
|
|
15
16
|
sourcecode/doc_analyzer.py,sha256=TttdS7mndKQhyJCfJnnAsyGCJrf-TIL7oXxDlTLUFKE,21248
|
|
16
17
|
sourcecode/entrypoint_classifier.py,sha256=a69dMGyxCTd_LOm3oqj-EXWpRmbmeujN7T1mr2eJ1as,3877
|
|
17
|
-
sourcecode/env_analyzer.py,sha256=
|
|
18
|
+
sourcecode/env_analyzer.py,sha256=YXlaxFBuf-ladWmb3iLCNMN-rKhP2JuqAIDwZdiIZHQ,18473
|
|
18
19
|
sourcecode/file_classifier.py,sha256=_KfFIIolharaIxbSTrCkaWauQIqNHCyor_n47RGyDh8,8577
|
|
19
20
|
sourcecode/git_analyzer.py,sha256=PD3eNWydznQ6KLNpxGzBqizIHoPIKevfwz9Xyf_pDt4,11600
|
|
20
21
|
sourcecode/graph_analyzer.py,sha256=hMOsLLz9B0UnQ4xwbHdgr3bFvqpw0bQ8kN-xmEn3Krk,64156
|
|
21
22
|
sourcecode/metrics_analyzer.py,sha256=e2cFwB9XubFq_dIVsP2PLjpr4wX0N6ulb3ol3sGDUeo,20777
|
|
22
|
-
sourcecode/prepare_context.py,sha256=
|
|
23
|
-
sourcecode/ranking_engine.py,sha256=
|
|
23
|
+
sourcecode/prepare_context.py,sha256=qmxMvTlteeEGwDaNDRoRj0iGY1D7goVD_yV1MVOeQkM,32261
|
|
24
|
+
sourcecode/ranking_engine.py,sha256=virVglafZufioHpZpwktjMvUiL0TZELWQCQnQNV8dFo,9360
|
|
24
25
|
sourcecode/redactor.py,sha256=xuGcadGEHaPw4qZXlMDvzMCsr4VOkdp3oBQptHyJk8c,2884
|
|
25
26
|
sourcecode/relevance_scorer.py,sha256=E74w7nlsNVobO3LqKHiMtBd84ONwGp8uDpwXJEjRtLA,8330
|
|
26
27
|
sourcecode/repo_classifier.py,sha256=FG1vaWKdWXsWdl-S8hjVMiTqcwgaRXkDyvK4rPcOGtQ,22681
|
|
@@ -28,7 +29,7 @@ sourcecode/runtime_classifier.py,sha256=zWX3r3HCKHc-qtIobErOa8aKMmaoPYREtJKvPcBG
|
|
|
28
29
|
sourcecode/scanner.py,sha256=aM3h9-DCQ3xKpeHpHYdo2vX6T5P95HA_YwZbkAVNwmo,8288
|
|
29
30
|
sourcecode/schema.py,sha256=ofEge9hTWHOTjeWt7ceCDQWzP-uhhenrYX2usjW2KVU,22759
|
|
30
31
|
sourcecode/semantic_analyzer.py,sha256=16EFTgM7ooW0m5gNUKOlTSn7IEMLSzKmzQn-cWaSqjs,82604
|
|
31
|
-
sourcecode/serializer.py,sha256=
|
|
32
|
+
sourcecode/serializer.py,sha256=CBK_i1joiNl8aCjFgnyIMrEFhAVCAk_ltfOqot2yVC4,59361
|
|
32
33
|
sourcecode/summarizer.py,sha256=ZuzIdm3t8A-d5MuQL0TSNLrd-L0IQIuguIxeNXMNJf8,16070
|
|
33
34
|
sourcecode/tree_utils.py,sha256=Fj9OIuUksBvgibNd3feog0sMDjVypJzPexp5lvMoYWI,1424
|
|
34
35
|
sourcecode/workspace.py,sha256=fQlVoNx8S-fSHpKoJ0JBvEHCFkxszH0KZVJed1i3TRk,6845
|
|
@@ -39,7 +40,7 @@ sourcecode/detectors/dart.py,sha256=QbqaL5v18-_ort75HihVBt8MsKUfOcFDF8IpWFLiXpI,
|
|
|
39
40
|
sourcecode/detectors/dotnet.py,sha256=oi8zq3AfUItlK3h_qM81vOe1ZVTIU9LBKIlIrRDuqOs,6864
|
|
40
41
|
sourcecode/detectors/elixir.py,sha256=jCpvt5Yi6jvplc80ovRtWh17q-11ZGo9qX7o8b57TJE,1713
|
|
41
42
|
sourcecode/detectors/go.py,sha256=2r66uRQfeTWsqxr4HDhT6vExZErby0t46QXLHVBRv9w,2782
|
|
42
|
-
sourcecode/detectors/heuristic.py,sha256=
|
|
43
|
+
sourcecode/detectors/heuristic.py,sha256=bCqqgbHavl4Sse3dqT8mwmo1wAdgeJr7VyXOmfClLKo,3387
|
|
43
44
|
sourcecode/detectors/hybrid.py,sha256=IGFRUVsAZ1ooRlFdznCeJAV6vy1yVDx-VyghvLtddXc,9101
|
|
44
45
|
sourcecode/detectors/java.py,sha256=cZvB13cqJ76zHDncEG-TOCuK8gJjJN2mZGS2DGEcZy8,7715
|
|
45
46
|
sourcecode/detectors/jvm_ext.py,sha256=EgHJ5W8EE-ZTN9V607mVzohyKgZE8Mc2jCi-DF8RAZU,2616
|
|
@@ -59,8 +60,8 @@ sourcecode/telemetry/consent.py,sha256=wLMvGNJeSSyZoNkQXpoUioY6mMv4Qdvuw7S9jAEWn
|
|
|
59
60
|
sourcecode/telemetry/events.py,sha256=oEvvulfsv5GIDWG2174gSS6tNB95w38AIYiYeifGKlE,2294
|
|
60
61
|
sourcecode/telemetry/filters.py,sha256=Asa71oRl7q3Wt_FMwuufIZJFzSYdgRNKS8LHCIyFeYE,4805
|
|
61
62
|
sourcecode/telemetry/transport.py,sha256=KJeIPCPWMdmbCP3ySGs2iUlia34U6vWne2dZsUezesw,1560
|
|
62
|
-
sourcecode-0.
|
|
63
|
-
sourcecode-0.
|
|
64
|
-
sourcecode-0.
|
|
65
|
-
sourcecode-0.
|
|
66
|
-
sourcecode-0.
|
|
63
|
+
sourcecode-0.45.0.dist-info/METADATA,sha256=KxuymZTpmCBseOiKT5iavKPp7S2zJaYxpB11QEQqnjk,25209
|
|
64
|
+
sourcecode-0.45.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
65
|
+
sourcecode-0.45.0.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
|
|
66
|
+
sourcecode-0.45.0.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
|
|
67
|
+
sourcecode-0.45.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|