sourcecode 1.30.17__py3-none-any.whl → 1.30.18__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/contract_pipeline.py +2 -0
- sourcecode/prepare_context.py +98 -29
- sourcecode/repository_ir.py +2 -0
- {sourcecode-1.30.17.dist-info → sourcecode-1.30.18.dist-info}/METADATA +3 -3
- {sourcecode-1.30.17.dist-info → sourcecode-1.30.18.dist-info}/RECORD +9 -9
- {sourcecode-1.30.17.dist-info → sourcecode-1.30.18.dist-info}/WHEEL +0 -0
- {sourcecode-1.30.17.dist-info → sourcecode-1.30.18.dist-info}/entry_points.txt +0 -0
- {sourcecode-1.30.17.dist-info → sourcecode-1.30.18.dist-info}/licenses/LICENSE +0 -0
sourcecode/__init__.py
CHANGED
sourcecode/contract_pipeline.py
CHANGED
sourcecode/prepare_context.py
CHANGED
|
@@ -561,17 +561,23 @@ _ARTIFACT_CHANGE_EFFECT: dict[str, str] = {
|
|
|
561
561
|
# Maps frontend symptom keywords → backend terms likely to contain the root cause.
|
|
562
562
|
# Used to boost service/interceptor files when the symptom is UI-only.
|
|
563
563
|
_FRONTEND_SYMPTOM_MAP: dict[str, list[str]] = {
|
|
564
|
-
"spinner":
|
|
565
|
-
"loading":
|
|
566
|
-
"login":
|
|
567
|
-
"logout":
|
|
568
|
-
"dropdown":
|
|
569
|
-
"modal":
|
|
570
|
-
"popup":
|
|
571
|
-
"table":
|
|
572
|
-
"grid":
|
|
573
|
-
"button":
|
|
574
|
-
"form":
|
|
564
|
+
"spinner": ["loading", "setloading", "finalize", "httpinterceptor", "interceptor", "service"],
|
|
565
|
+
"loading": ["loading", "setloading", "finalize", "httpinterceptor", "interceptor", "service"],
|
|
566
|
+
"login": ["authcontroller", "securityconfig", "filterconfig", "jwtfilter", "auth", "authentication"],
|
|
567
|
+
"logout": ["authcontroller", "securityconfig", "jwtfilter", "auth", "session"],
|
|
568
|
+
"dropdown": ["getmapping", "findall", "obtenertodos", "listall", "findby"],
|
|
569
|
+
"modal": ["controller", "getmapping", "findby", "search"],
|
|
570
|
+
"popup": ["controller", "getmapping", "findby", "search"],
|
|
571
|
+
"table": ["paginated", "findby", "search", "getmapping", "listall"],
|
|
572
|
+
"grid": ["paginated", "findby", "search", "getmapping"],
|
|
573
|
+
"button": ["postmapping", "putmapping", "deletemapping", "controller", "service"],
|
|
574
|
+
"form": ["postmapping", "putmapping", "controller", "service", "dto"],
|
|
575
|
+
# session-related symptoms (Spanish + English)
|
|
576
|
+
"sesion": ["httpsession", "sessionmanager", "sessionservice", "sessionrepository", "sessionfactory", "authentication"],
|
|
577
|
+
"sesiones": ["httpsession", "sessionmanager", "sessionservice", "sessionrepository", "sessionfactory", "authentication"],
|
|
578
|
+
"session": ["httpsession", "sessionmanager", "sessionservice", "sessionrepository", "sessionfactory", "authentication"],
|
|
579
|
+
# worker/assignment domain terms (common in RRHH/HR systems)
|
|
580
|
+
"trabajador": ["trabajador", "empleado", "worker", "asignacion", "trabajadordao", "trabajadorservice"],
|
|
575
581
|
}
|
|
576
582
|
|
|
577
583
|
|
|
@@ -826,12 +832,14 @@ class TaskContextBuilder:
|
|
|
826
832
|
# ── 5b. Git signals for ranking ────────────────────────────────────
|
|
827
833
|
git_hotspots: dict[str, int] = {}
|
|
828
834
|
uncommitted_files: set[str] = set()
|
|
835
|
+
_recent_commits_for_symptom: list = []
|
|
829
836
|
try:
|
|
830
837
|
from sourcecode.git_analyzer import GitAnalyzer
|
|
831
838
|
_gc = GitAnalyzer().analyze(self.root, depth=30, days=90)
|
|
832
839
|
_bad = {"no_git_repo", "git_not_found", "git_timeout"}
|
|
833
840
|
if _gc and not (_bad & set(_gc.limitations)):
|
|
834
841
|
git_hotspots = {h.file: h.commit_count for h in _gc.change_hotspots}
|
|
842
|
+
_recent_commits_for_symptom = list(_gc.recent_commits)
|
|
835
843
|
if _gc.uncommitted_changes:
|
|
836
844
|
_uc = _gc.uncommitted_changes
|
|
837
845
|
uncommitted_files = set(_uc.staged) | set(_uc.unstaged)
|
|
@@ -1208,19 +1216,52 @@ class TaskContextBuilder:
|
|
|
1208
1216
|
if len(w) > 2
|
|
1209
1217
|
]
|
|
1210
1218
|
if symptom_keywords:
|
|
1211
|
-
#
|
|
1219
|
+
# Pass 1: surface code notes whose text contains any keyword
|
|
1220
|
+
# Also track which file paths have matching notes (for score boost below).
|
|
1221
|
+
_note_matched_paths: dict[str, int] = {} # path → count of matching notes
|
|
1212
1222
|
for _n in cn_notes_for_ranking:
|
|
1213
1223
|
_text = (getattr(_n, "text", "") or "").lower()
|
|
1214
1224
|
if any(kw in _text for kw in symptom_keywords):
|
|
1225
|
+
_np = getattr(_n, "path", "")
|
|
1215
1226
|
related_notes.append({
|
|
1216
1227
|
"kind": getattr(_n, "kind", ""),
|
|
1217
|
-
"path":
|
|
1228
|
+
"path": _np,
|
|
1218
1229
|
"line": getattr(_n, "line", None),
|
|
1219
1230
|
"text": getattr(_n, "text", ""),
|
|
1220
1231
|
})
|
|
1221
|
-
|
|
1222
|
-
|
|
1232
|
+
_note_matched_paths[_np] = _note_matched_paths.get(_np, 0) + 1
|
|
1233
|
+
|
|
1234
|
+
# Pass 2: build commit message index — files touched in commits whose
|
|
1235
|
+
# message matches a symptom keyword get a strong recency signal.
|
|
1236
|
+
# This is the primary signal for functional keywords like "sesiones"
|
|
1237
|
+
# that don't appear in file paths but do appear in commit messages.
|
|
1238
|
+
_commit_file_hits: dict[str, int] = {} # path → n matching commits
|
|
1239
|
+
for _cr in _recent_commits_for_symptom:
|
|
1240
|
+
_msg_lower = (_cr.message or "").lower()
|
|
1241
|
+
if any(kw in _msg_lower for kw in symptom_keywords):
|
|
1242
|
+
for _cf in (_cr.files_changed or []):
|
|
1243
|
+
_cf_norm = _cf.replace("\\", "/")
|
|
1244
|
+
_commit_file_hits[_cf_norm] = _commit_file_hits.get(_cf_norm, 0) + 1
|
|
1245
|
+
|
|
1246
|
+
# Pass 3: inject files from commit index not yet in candidate pool
|
|
1223
1247
|
_existing_paths = {rf.path for rf in relevant_files}
|
|
1248
|
+
for _cp, _nhits in _commit_file_hits.items():
|
|
1249
|
+
if _cp in _existing_paths:
|
|
1250
|
+
continue
|
|
1251
|
+
if Path(_cp).suffix.lower() not in _ALL_EXTENSIONS:
|
|
1252
|
+
continue
|
|
1253
|
+
_ci_score = round(min(0.5 + 0.15 * _nhits, 0.85), 2)
|
|
1254
|
+
relevant_files.append(RelevantFile(
|
|
1255
|
+
path=_cp,
|
|
1256
|
+
role="symptom_match",
|
|
1257
|
+
score=_ci_score,
|
|
1258
|
+
reason=f"commit message matches symptom ({_nhits} commit{'s' if _nhits > 1 else ''})",
|
|
1259
|
+
why=f"symptom commit-index: {', '.join(symptom_keywords)}",
|
|
1260
|
+
))
|
|
1261
|
+
_existing_paths.add(_cp)
|
|
1262
|
+
|
|
1263
|
+
# Pass 4: inject files whose path matches symptom keywords
|
|
1264
|
+
# but weren't in the candidate pool (no structural/git signals).
|
|
1224
1265
|
for _p in all_paths:
|
|
1225
1266
|
if _p in _existing_paths:
|
|
1226
1267
|
continue
|
|
@@ -1242,17 +1283,38 @@ class TaskContextBuilder:
|
|
|
1242
1283
|
))
|
|
1243
1284
|
_existing_paths.add(_p)
|
|
1244
1285
|
|
|
1245
|
-
#
|
|
1246
|
-
|
|
1247
|
-
path_lower = rf.path.lower()
|
|
1248
|
-
return rf.score + 0.2 * sum(1.0 for kw in symptom_keywords if kw in path_lower)
|
|
1249
|
-
relevant_files = sorted(relevant_files, key=lambda rf: -_symptom_score(rf))
|
|
1250
|
-
|
|
1251
|
-
# Content scan boost: read file body for symptom keywords
|
|
1286
|
+
# Pass 5: multi-signal boost — apply commit, note, content, and path
|
|
1287
|
+
# signals in one pass to avoid redundant file reads.
|
|
1252
1288
|
_src_exts = frozenset({".java", ".py", ".ts", ".js", ".kt", ".go"})
|
|
1253
|
-
|
|
1289
|
+
_boosted: list[RelevantFile] = []
|
|
1254
1290
|
for _rf in relevant_files:
|
|
1255
1291
|
_extra = 0.0
|
|
1292
|
+
_reasons: list[str] = []
|
|
1293
|
+
_p_lower = _rf.path.lower()
|
|
1294
|
+
|
|
1295
|
+
# Commit message boost: +0.25/commit, cap +0.40
|
|
1296
|
+
_c_hits = _commit_file_hits.get(_rf.path, 0)
|
|
1297
|
+
if _c_hits:
|
|
1298
|
+
_cb = min(0.40, _c_hits * 0.25)
|
|
1299
|
+
_extra += _cb
|
|
1300
|
+
_reasons.append(f"commit-msg symptom ×{_c_hits} (+{_cb:.2f})")
|
|
1301
|
+
|
|
1302
|
+
# Code note boost: +0.20/note, cap +0.30
|
|
1303
|
+
_n_hits = _note_matched_paths.get(_rf.path, 0)
|
|
1304
|
+
if _n_hits:
|
|
1305
|
+
_nb = min(0.30, _n_hits * 0.20)
|
|
1306
|
+
_extra += _nb
|
|
1307
|
+
_reasons.append(f"note-match symptom ×{_n_hits} (+{_nb:.2f})")
|
|
1308
|
+
|
|
1309
|
+
# Path keyword boost: +0.20/keyword already in score for injected
|
|
1310
|
+
# files; re-apply for pre-existing candidates whose path matches.
|
|
1311
|
+
_path_kws = [kw for kw in symptom_keywords if kw in _p_lower]
|
|
1312
|
+
if _path_kws and _rf.role != "symptom_match":
|
|
1313
|
+
_pb = 0.20 * len(_path_kws)
|
|
1314
|
+
_extra += _pb
|
|
1315
|
+
_reasons.append(f"path-kw symptom ({', '.join(_path_kws)}) (+{_pb:.2f})")
|
|
1316
|
+
|
|
1317
|
+
# Content scan boost: +0.05/hit, cap +0.50 (was +0.02, cap +0.30)
|
|
1256
1318
|
if Path(_rf.path).suffix.lower() in _src_exts:
|
|
1257
1319
|
try:
|
|
1258
1320
|
_lines = (self.root / _rf.path).read_text(
|
|
@@ -1260,26 +1322,33 @@ class TaskContextBuilder:
|
|
|
1260
1322
|
).splitlines()[:300]
|
|
1261
1323
|
_body = "\n".join(_lines).lower()
|
|
1262
1324
|
_hits = sum(_body.count(kw) for kw in symptom_keywords)
|
|
1263
|
-
|
|
1325
|
+
_content_b = min(0.50, _hits * 0.05)
|
|
1326
|
+
if _content_b > 0:
|
|
1327
|
+
_extra += _content_b
|
|
1328
|
+
_reasons.append(f"content-match symptom ×{_hits} (+{_content_b:.2f})")
|
|
1264
1329
|
except OSError:
|
|
1265
1330
|
pass
|
|
1266
|
-
|
|
1331
|
+
|
|
1332
|
+
_new_reason = _rf.reason
|
|
1333
|
+
if _reasons:
|
|
1334
|
+
_new_reason = _rf.reason + ", " + ", ".join(_reasons)
|
|
1335
|
+
_boosted.append(RelevantFile(
|
|
1267
1336
|
path=_rf.path,
|
|
1268
1337
|
role=_rf.role,
|
|
1269
1338
|
score=round(min(_rf.score + _extra, 1.0), 2),
|
|
1270
|
-
reason=
|
|
1339
|
+
reason=_new_reason,
|
|
1271
1340
|
why=_rf.why,
|
|
1272
1341
|
))
|
|
1273
|
-
relevant_files = sorted(
|
|
1342
|
+
relevant_files = sorted(_boosted, key=lambda rf: -rf.score)
|
|
1274
1343
|
|
|
1275
|
-
#
|
|
1344
|
+
# Pass 6: cross-layer synonym boost — frontend keywords → backend equivalents
|
|
1276
1345
|
_synonym_note: Optional[str] = None
|
|
1277
1346
|
_frontend_kws = [kw for kw in symptom_keywords if kw in _FRONTEND_SYMPTOM_MAP]
|
|
1278
1347
|
if _frontend_kws:
|
|
1279
1348
|
_backend_terms: list[str] = []
|
|
1280
1349
|
for _fkw in _frontend_kws:
|
|
1281
1350
|
_backend_terms.extend(_FRONTEND_SYMPTOM_MAP[_fkw])
|
|
1282
|
-
_backend_terms_set = list(dict.fromkeys(_backend_terms))
|
|
1351
|
+
_backend_terms_set = list(dict.fromkeys(_backend_terms))
|
|
1283
1352
|
_synonym_boosted: list[RelevantFile] = []
|
|
1284
1353
|
for _rf in relevant_files:
|
|
1285
1354
|
_extra_syn = 0.0
|
sourcecode/repository_ir.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 1.30.
|
|
3
|
+
Version: 1.30.18
|
|
4
4
|
Summary: Deterministic codebase context for AI coding agents
|
|
5
5
|
License: Apache License
|
|
6
6
|
Version 2.0, January 2004
|
|
@@ -221,7 +221,7 @@ Description-Content-Type: text/markdown
|
|
|
221
221
|
|
|
222
222
|
**Deterministic, behavior-aware codebase context for AI agents and PR review.**
|
|
223
223
|
|
|
224
|
-

|
|
225
225
|

|
|
226
226
|
|
|
227
227
|
---
|
|
@@ -257,7 +257,7 @@ pipx install sourcecode
|
|
|
257
257
|
|
|
258
258
|
```bash
|
|
259
259
|
sourcecode version
|
|
260
|
-
# sourcecode 1.30.
|
|
260
|
+
# sourcecode 1.30.18
|
|
261
261
|
```
|
|
262
262
|
|
|
263
263
|
---
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
sourcecode/__init__.py,sha256=
|
|
1
|
+
sourcecode/__init__.py,sha256=6mHUCAZLGN6amZwcIEqBuvLyiGaJroesk9TR_32ZpWo,104
|
|
2
2
|
sourcecode/adaptive_scanner.py,sha256=RTNExwWPXzjgLaRueT7UuxkPj5ZEToWjGbx1j0LSZ9E,10250
|
|
3
3
|
sourcecode/architecture_analyzer.py,sha256=MyBa0Hf5HmkudZQDLKrjcWDKETXETXl0mQX1swtTwAA,39091
|
|
4
4
|
sourcecode/architecture_summary.py,sha256=z34_6v7cSwy98cof2UVciGho7SCrZ93tiqMmq5WNzRQ,20405
|
|
@@ -10,7 +10,7 @@ sourcecode/confidence_analyzer.py,sha256=xw_Jv8pAd0wd8t2vvQlorw8Ih0rSF3YCoFS8K-_
|
|
|
10
10
|
sourcecode/context_scorer.py,sha256=QpChSpsmaAYz91rXA4Ue5xzQmNz_ZboZN09YOHScq1U,14679
|
|
11
11
|
sourcecode/context_summarizer.py,sha256=CiQrfBEzun949bWvmLabWoj2HhPn6Lw62ofqnsy0FlQ,6503
|
|
12
12
|
sourcecode/contract_model.py,sha256=nRxJKPMs1VHwFTa8AVXhGmaLjti3Lr2sjHDpWgv1bfE,3917
|
|
13
|
-
sourcecode/contract_pipeline.py,sha256=
|
|
13
|
+
sourcecode/contract_pipeline.py,sha256=w18t_MdbrkIeLcCW-VMQYeb9hlWenAOB-NMiME_Fo-Y,27652
|
|
14
14
|
sourcecode/coverage_parser.py,sha256=q0LeZJaX1bnntLu-ImksdBsMlpsVmk_iUfSaB4eaJGo,19702
|
|
15
15
|
sourcecode/dependency_analyzer.py,sha256=p4ljXhkcGBbFlhaZuPrsjOVjDXaKLTg0Gor2p4qFPP0,56208
|
|
16
16
|
sourcecode/doc_analyzer.py,sha256=afA4uJFwXZ_uR2l4J0pQwbeTkRkGmKdN9KhRVYePBUw,24331
|
|
@@ -22,13 +22,13 @@ sourcecode/git_analyzer.py,sha256=_pCg2V4d2aa17k9hayTzpexAj8syvyk4y9NYNvvgOAI,12
|
|
|
22
22
|
sourcecode/graph_analyzer.py,sha256=iUK-7pSV-cvGqqD2hENdYmhnm0wcXFEyK-xnu5ul8OU,62515
|
|
23
23
|
sourcecode/metrics_analyzer.py,sha256=m0ENgtqKeBL17kUIK3fmGkgo7UfXBNHxCMj0H_Y5K7c,22750
|
|
24
24
|
sourcecode/pr_comment_renderer.py,sha256=k5pCIP6iBNwy5UYPxu47CAq-62j4E9QZZOPyL3trH80,14799
|
|
25
|
-
sourcecode/prepare_context.py,sha256=
|
|
25
|
+
sourcecode/prepare_context.py,sha256=W4cK5EESAQWyLKWobuzt3X9Ca7kZp1ui8efD0bgTzEE,156227
|
|
26
26
|
sourcecode/progress.py,sha256=qn30sWaHOkjTgXsSBmiPkz7Rsbwc5oSlIe6JNEMYp_k,3149
|
|
27
27
|
sourcecode/ranking_engine.py,sha256=virVglafZufioHpZpwktjMvUiL0TZELWQCQnQNV8dFo,9360
|
|
28
28
|
sourcecode/redactor.py,sha256=xuGcadGEHaPw4qZXlMDvzMCsr4VOkdp3oBQptHyJk8c,2884
|
|
29
29
|
sourcecode/relevance_scorer.py,sha256=MYF4FFkveAQps9SmTeTlh6ODiBz2F--_hWNeHMLtUHQ,8405
|
|
30
30
|
sourcecode/repo_classifier.py,sha256=FG1vaWKdWXsWdl-S8hjVMiTqcwgaRXkDyvK4rPcOGtQ,22681
|
|
31
|
-
sourcecode/repository_ir.py,sha256=
|
|
31
|
+
sourcecode/repository_ir.py,sha256=KMB_5sPtV695CaVzH-dOl9j9_zvP45PjzGZJiZzrdAM,57626
|
|
32
32
|
sourcecode/runtime_classifier.py,sha256=zWX3r3HCKHc-qtIobErOa8aKMmaoPYREtJKvPcBGPjQ,14792
|
|
33
33
|
sourcecode/scanner.py,sha256=aM3h9-DCQ3xKpeHpHYdo2vX6T5P95HA_YwZbkAVNwmo,8288
|
|
34
34
|
sourcecode/schema.py,sha256=fj3BZ3IcnNV4j21BFIEvz8Qnw_vZoqIbzzRg-qQ-nd0,24530
|
|
@@ -64,8 +64,8 @@ sourcecode/telemetry/consent.py,sha256=wLMvGNJeSSyZoNkQXpoUioY6mMv4Qdvuw7S9jAEWn
|
|
|
64
64
|
sourcecode/telemetry/events.py,sha256=oEvvulfsv5GIDWG2174gSS6tNB95w38AIYiYeifGKlE,2294
|
|
65
65
|
sourcecode/telemetry/filters.py,sha256=Asa71oRl7q3Wt_FMwuufIZJFzSYdgRNKS8LHCIyFeYE,4805
|
|
66
66
|
sourcecode/telemetry/transport.py,sha256=KJeIPCPWMdmbCP3ySGs2iUlia34U6vWne2dZsUezesw,1560
|
|
67
|
-
sourcecode-1.30.
|
|
68
|
-
sourcecode-1.30.
|
|
69
|
-
sourcecode-1.30.
|
|
70
|
-
sourcecode-1.30.
|
|
71
|
-
sourcecode-1.30.
|
|
67
|
+
sourcecode-1.30.18.dist-info/METADATA,sha256=lqlAi_PtaPLAr3s8RUw0ULhaKZioUxzdNsVBKKVfRqc,28956
|
|
68
|
+
sourcecode-1.30.18.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
69
|
+
sourcecode-1.30.18.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
|
|
70
|
+
sourcecode-1.30.18.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
|
|
71
|
+
sourcecode-1.30.18.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|