sourcecode 1.5.0__py3-none-any.whl → 1.6.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 CHANGED
@@ -1,3 +1,3 @@
1
1
  """sourcecode — Deterministic codebase context maps for AI coding agents."""
2
2
 
3
- __version__ = "1.5.0"
3
+ __version__ = "1.6.0"
sourcecode/cli.py CHANGED
@@ -1399,12 +1399,6 @@ def main(
1399
1399
  ))
1400
1400
  sm = _replace(sm, pipeline_trace=_trace.build_trace())
1401
1401
 
1402
- # P3-B: Auto-switch to centrality ranking when DDD layout detected
1403
- if (rank_by == "relevance"
1404
- and sm.architecture is not None
1405
- and sm.architecture.pattern == "ddd"):
1406
- rank_by = "centrality"
1407
-
1408
1402
  # Contract pipeline — runs for mode=contract|standard|deep|hybrid (skip for raw)
1409
1403
  _is_contract_mode = mode in ("contract", "standard")
1410
1404
  _pipeline_error = False
@@ -240,10 +240,18 @@ class ContractPipeline:
240
240
 
241
241
  # Apply max_files cap — bypass when symbol search to ensure defining files are found.
242
242
  # A symbol query over a large repo needs all files; result set is small after filtering.
243
+ # MyBatis Mapper.xml contracts rank below Java files on path score alone (.xml has no
244
+ # suffix boost). Give them the same priority slot as entry_points so they survive the cap.
245
+ def _is_priority(p: str) -> bool:
246
+ if p in entry_paths:
247
+ return True
248
+ name = p.rsplit("/", 1)[-1]
249
+ return name.lower().endswith("mapper.xml")
250
+
243
251
  if symbol is None and len(src_paths) > self.max_files:
244
252
  src_paths = sorted(
245
253
  src_paths,
246
- key=lambda p: (p in entry_paths, scorer.score(p)),
254
+ key=lambda p: (_is_priority(p), scorer.score(p)),
247
255
  reverse=True,
248
256
  )[:self.max_files]
249
257
 
@@ -362,7 +370,9 @@ class ContractPipeline:
362
370
 
363
371
  def _rank(self, contracts: list[FileContract], rank_by: RankStrategy) -> list[FileContract]:
364
372
  if rank_by == "centrality":
365
- return sorted(contracts, key=lambda c: (-(c.fan_in + c.fan_out), c.path))
373
+ # Entrypoints (REST controllers, main classes) surface first even in centrality mode:
374
+ # they have low fan_in (not imported) but are the primary API surface.
375
+ return sorted(contracts, key=lambda c: (-c.is_entrypoint, -(c.fan_in + c.fan_out), c.path))
366
376
  if rank_by == "git-churn":
367
377
  return sorted(contracts, key=lambda c: (-c.is_changed, -c.relevance_score, c.path))
368
378
  # Default: relevance — path breaks ties deterministically
@@ -225,6 +225,12 @@ class JavaDetector(AbstractDetector):
225
225
  all_paths = flatten_file_tree(context.file_tree)
226
226
  all_java = [p for p in all_paths if p.endswith(".java")]
227
227
 
228
+ # Augment with a direct scan of standard Java source roots for Controller-named
229
+ # files that the depth-limited file_tree scanner may have missed.
230
+ # DDD layouts place REST controllers at depth 10+ (e.g.
231
+ # src/main/java/com/org/app/ddd/domain/infraestructure/rest/XxxRestController.java).
232
+ self._augment_deep_java_controllers(context, all_java)
233
+
228
234
  # 1. @SpringBootApplication entry: Application.java / Main.java by name
229
235
  app_candidates = [
230
236
  p for p in all_java
@@ -270,6 +276,40 @@ class JavaDetector(AbstractDetector):
270
276
  unique_eps.append(ep)
271
277
  return unique_eps
272
278
 
279
+ def _augment_deep_java_controllers(self, context: DetectionContext, all_java: list[str]) -> None:
280
+ """Scan standard Java source roots for *Controller*.java files not in all_java.
281
+
282
+ The depth-limited file_tree scanner misses files at depth >= max_depth.
283
+ DDD layouts place REST controllers deep (e.g. depth 10+), so we supplement
284
+ with a direct filesystem walk scoped to the standard Maven/Gradle source root.
285
+ """
286
+ import os as _os
287
+ existing = set(all_java)
288
+ # Standard Java source root candidates (Maven first, then Gradle/other)
289
+ _SRC_ROOTS = ("src/main/java", "src/main/kotlin", "src/java", "src")
290
+ for src_root_name in _SRC_ROOTS:
291
+ src_root = context.root / src_root_name
292
+ if not src_root.is_dir():
293
+ continue
294
+ try:
295
+ for dirpath, _dirs, filenames in _os.walk(str(src_root)):
296
+ for fname in filenames:
297
+ if "Controller" not in fname or not fname.endswith(".java"):
298
+ continue
299
+ full = Path(dirpath) / fname
300
+ if full.is_symlink():
301
+ continue
302
+ try:
303
+ rel = str(full.relative_to(context.root)).replace("\\", "/")
304
+ if rel not in existing:
305
+ all_java.append(rel)
306
+ existing.add(rel)
307
+ except ValueError:
308
+ pass
309
+ except OSError:
310
+ pass
311
+ return # use only first matching source root
312
+
273
313
  def _scan_java_file_for_entry_points(self, abs_path: Path, rel_path: str) -> list[EntryPoint]:
274
314
  try:
275
315
  if abs_path.stat().st_size > _MAX_FILE_SIZE:
sourcecode/serializer.py CHANGED
@@ -1110,6 +1110,10 @@ def standard_view(sm: SourceMap, *, include_tree: bool = False) -> dict[str, Any
1110
1110
 
1111
1111
  if sm.semantic_summary is not None and sm.semantic_summary.requested:
1112
1112
  result["semantic_summary"] = asdict(sm.semantic_summary)
1113
+ # Backward compat: also emit hotspots at top level (moved to semantic_summary in v1.5.0).
1114
+ # Consumers reading d["hotspots"] directly still work.
1115
+ if sm.semantic_summary.hotspots:
1116
+ result["hotspots"] = sm.semantic_summary.hotspots[:10]
1113
1117
  # Defensive filter: never emit objects with null required fields.
1114
1118
  # A null entry in these arrays is worse than a shorter array — it causes
1115
1119
  # agents to misinterpret the analysis as valid when it is not.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 1.5.0
3
+ Version: 1.6.0
4
4
  Summary: Deterministic codebase context for AI coding agents
5
5
  License: Apache License
6
6
  Version 2.0, January 2004
@@ -1,16 +1,16 @@
1
- sourcecode/__init__.py,sha256=81eXvIk2uxc6dFJg9ND6pJ-xePSGbA0ZUJgr1h_bZ48,102
1
+ sourcecode/__init__.py,sha256=s3ubr020DVq1rHjxHihNLq1cecOGqF6x7czXl3I96Sk,102
2
2
  sourcecode/adaptive_scanner.py,sha256=6dh34C2qZXyRbw-8xBhbEwDdXanM6CRFRWayVoYITnA,10190
3
3
  sourcecode/architecture_analyzer.py,sha256=oPmGPf9_p6y8Z7SIGHEu57nVYwxycIovDzBvlHl-l5k,32557
4
4
  sourcecode/architecture_summary.py,sha256=J9yoLgh8wXwIRrT6q6JooB6PekivbOEYpJz4BUXdalk,20545
5
5
  sourcecode/ast_extractor.py,sha256=XgrZg2DcWcUm9r87cRG3KGO7IK2TIL_N-CvhSbUmmh4,49901
6
6
  sourcecode/classifier.py,sha256=GKTMN8qKZX7ponSwDJfN08RrasI4CVpq1_gFBgEopps,7093
7
- sourcecode/cli.py,sha256=_oYoP4_AAFGw0gg-FBGn_DKh7y49xmwuL6D_Bafl2w4,74497
7
+ sourcecode/cli.py,sha256=91GUbfLS86RMTurkhoMWFUPqby30AEgzkLDIvJui9Fc,74262
8
8
  sourcecode/code_notes_analyzer.py,sha256=rRd8bFYV0krjlxxQV0wenwE9K7pVpUQSR7KvSvUQKw4,9226
9
9
  sourcecode/confidence_analyzer.py,sha256=HcaewB2pZaZ_hfKrZWtr_yPMY2-CxS1zzTUD7c4argc,13188
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=_mZn0GD7UjjJ7E79DmmYjF0sVkuOLBZEgZHqgXVyTrs,26648
13
+ sourcecode/contract_pipeline.py,sha256=GX-AyUxULywnGHYpmVlMDGsiHEsdegGUHfLVn7Ffvis,27238
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=a1CIClCNmfYM3ku4bdgwHQpmb6Js4wdJZ1V5EYLo04I,24345
@@ -29,7 +29,7 @@ sourcecode/runtime_classifier.py,sha256=zWX3r3HCKHc-qtIobErOa8aKMmaoPYREtJKvPcBG
29
29
  sourcecode/scanner.py,sha256=aM3h9-DCQ3xKpeHpHYdo2vX6T5P95HA_YwZbkAVNwmo,8288
30
30
  sourcecode/schema.py,sha256=rNxpDUgOfvJDzz6DKitL-5_0UAh0YomNwTsSSe8PafY,24066
31
31
  sourcecode/semantic_analyzer.py,sha256=12TwXYkYbDcBdu0heX_EmfPM2EkO8a_r5osf0SaeQbs,88956
32
- sourcecode/serializer.py,sha256=2ztWD4FCebPD0D5tY-kNgLlsATaQsPrSQY6rLQeemRI,69467
32
+ sourcecode/serializer.py,sha256=6dHvWc-iqqCjpu9-FcAXqtpKEO4mO3AZVzWT7Tphajo,69736
33
33
  sourcecode/summarizer.py,sha256=ZuzIdm3t8A-d5MuQL0TSNLrd-L0IQIuguIxeNXMNJf8,16070
34
34
  sourcecode/tree_utils.py,sha256=Fj9OIuUksBvgibNd3feog0sMDjVypJzPexp5lvMoYWI,1424
35
35
  sourcecode/workspace.py,sha256=X_6NmNnitvT3_38V-JDChydo_sR68s249hLFlrQskU0,8271
@@ -42,7 +42,7 @@ sourcecode/detectors/elixir.py,sha256=jCpvt5Yi6jvplc80ovRtWh17q-11ZGo9qX7o8b57TJ
42
42
  sourcecode/detectors/go.py,sha256=2r66uRQfeTWsqxr4HDhT6vExZErby0t46QXLHVBRv9w,2782
43
43
  sourcecode/detectors/heuristic.py,sha256=bCqqgbHavl4Sse3dqT8mwmo1wAdgeJr7VyXOmfClLKo,3387
44
44
  sourcecode/detectors/hybrid.py,sha256=IGFRUVsAZ1ooRlFdznCeJAV6vy1yVDx-VyghvLtddXc,9101
45
- sourcecode/detectors/java.py,sha256=0NKsy1uls5wvQSK7DJP_fM-v5_uuMEr_an47caelLHE,16612
45
+ sourcecode/detectors/java.py,sha256=XAnVOmq7yXgO_4UGlIFIVgNRtyWZQEVsDDu3sa5mTsk,18669
46
46
  sourcecode/detectors/jvm_ext.py,sha256=EgHJ5W8EE-ZTN9V607mVzohyKgZE8Mc2jCi-DF8RAZU,2616
47
47
  sourcecode/detectors/nodejs.py,sha256=7fsyAmrGkkguX6U80HUQpIe9MRaYyi_A7zbaRtmFmGc,13097
48
48
  sourcecode/detectors/parsers.py,sha256=ugPg8yNUf0Ai1gA7Fnn6wAkYGFjTxRodSP3IeViYJJ4,2290
@@ -60,8 +60,8 @@ sourcecode/telemetry/consent.py,sha256=wLMvGNJeSSyZoNkQXpoUioY6mMv4Qdvuw7S9jAEWn
60
60
  sourcecode/telemetry/events.py,sha256=oEvvulfsv5GIDWG2174gSS6tNB95w38AIYiYeifGKlE,2294
61
61
  sourcecode/telemetry/filters.py,sha256=Asa71oRl7q3Wt_FMwuufIZJFzSYdgRNKS8LHCIyFeYE,4805
62
62
  sourcecode/telemetry/transport.py,sha256=KJeIPCPWMdmbCP3ySGs2iUlia34U6vWne2dZsUezesw,1560
63
- sourcecode-1.5.0.dist-info/METADATA,sha256=kdZtCFuIhrWj6KDM4nZ-tMqWzuwiY2hXt4C8hP6PETc,20411
64
- sourcecode-1.5.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
65
- sourcecode-1.5.0.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
66
- sourcecode-1.5.0.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
67
- sourcecode-1.5.0.dist-info/RECORD,,
63
+ sourcecode-1.6.0.dist-info/METADATA,sha256=hB2-2Dm1fOj4fkPULr9PEH3aSLhD7ni4cPDxGAjmgB0,20411
64
+ sourcecode-1.6.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
65
+ sourcecode-1.6.0.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
66
+ sourcecode-1.6.0.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
67
+ sourcecode-1.6.0.dist-info/RECORD,,