sourcecode 0.44.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 CHANGED
@@ -1,3 +1,3 @@
1
1
  """sourcecode — Deterministic codebase context maps for AI coding agents."""
2
2
 
3
- __version__ = "0.44.0"
3
+ __version__ = "0.45.0"
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
- sm.key_dependencies = sorted(direct_deps, key=_dep_sort_key)[:15]
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,32 +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
- _contracts, _contract_summary = _cp.run(
1361
- target,
1362
- sm.file_paths,
1363
- entry_points=sm.entry_points,
1364
- monorepo_packages=sm.monorepo_packages,
1365
- mode=mode,
1366
- rank_by=rank_by, # type: ignore[arg-type]
1367
- max_symbols=max_symbols,
1368
- dependency_depth=dependency_depth,
1369
- entrypoints_only=entrypoints_only,
1370
- changed_only=changed_only,
1371
- symbol=symbol,
1372
- compress_types=compress_types,
1373
- max_importers=max_importers,
1374
- semantic_calls=sm.semantic_calls or None,
1375
- code_notes=sm.code_notes or None,
1376
- )
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
+ )
1377
1412
  sm = _replace(sm, file_contracts=_contracts, contract_summary=_contract_summary)
1378
1413
  if symbol is not None and len(_contracts) == 0:
1379
- typer.echo(
1380
- f"[warning] --symbol '{symbol}' matched 0 files. "
1381
- "The symbol may not exist at the current --depth, or the name may differ in case. "
1382
- "Try --depth 8 or verify the symbol name.",
1383
- err=True,
1384
- )
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
+ )
1385
1432
  if agent:
1386
1433
  typer.echo(f"[contract] {len(_contracts)} files extracted ({_contract_summary.method_breakdown})", err=True)
1387
1434
 
@@ -241,7 +241,9 @@ class ContractPipeline:
241
241
  contracts.append(contract)
242
242
  method_counts[contract.extraction_method] += 1
243
243
 
244
- if not self._extractor.has_tree_sitter():
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():
245
247
  limitations.append(
246
248
  "tree_sitter_unavailable: JS/TS extraction uses heuristics. "
247
249
  "Install with: pip install 'sourcecode[ast]'"
@@ -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, _count in counts.most_common()
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] = []
@@ -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", ".tox", ".eggs", "coverage",
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/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
- suspect = dict(item)
126
- suspect["reason"] = "declared as production dependency but no static import observed"
127
- groups["suspicious_dependencies"].append(suspect)
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:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 0.44.0
3
+ Version: 0.45.0
4
4
  Summary: Deterministic codebase context for AI coding agents
5
5
  License: Apache License
6
6
  Version 2.0, January 2004
@@ -1,21 +1,21 @@
1
- sourcecode/__init__.py,sha256=2Lz--0KUPsKNbk6_eT1ko8NRsFtoDHnAHBo9uNtCIDo,103
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=ON1YG8WzJ0Nb-zmlf65AzAgyCqkL1iiprJo2jwz6Pqk,68885
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
10
  sourcecode/context_scorer.py,sha256=nhppAo80fblAqcB9Ns0iQd21TZUrl2mQMo_xzPgavRE,14679
11
11
  sourcecode/context_summarizer.py,sha256=CiQrfBEzun949bWvmLabWoj2HhPn6Lw62ofqnsy0FlQ,6503
12
12
  sourcecode/contract_model.py,sha256=gCf9-Kj0G7l0lvRTAcRfFAfMgs1Rpizv4mKovQLYUkw,3434
13
- sourcecode/contract_pipeline.py,sha256=C3TJycL7pMRku7HQ5YbNFXxEZywtPQm8YaASRbYjs2g,24454
13
+ sourcecode/contract_pipeline.py,sha256=wWkm0_zScUy-Q-mrAG57qysYxjNPOybjObxpwp859e0,24615
14
14
  sourcecode/coverage_parser.py,sha256=q0LeZJaX1bnntLu-ImksdBsMlpsVmk_iUfSaB4eaJGo,19702
15
15
  sourcecode/dependency_analyzer.py,sha256=Exq0BfInvfS5iAg9xAr6WI2uPNuotkIudTKcYJcRhB8,52757
16
16
  sourcecode/doc_analyzer.py,sha256=TttdS7mndKQhyJCfJnnAsyGCJrf-TIL7oXxDlTLUFKE,21248
17
17
  sourcecode/entrypoint_classifier.py,sha256=a69dMGyxCTd_LOm3oqj-EXWpRmbmeujN7T1mr2eJ1as,3877
18
- sourcecode/env_analyzer.py,sha256=NFV4PSeBH5GEONOIo2SY5iJRXuuqhAOlRDtTZMqOZTI,18452
18
+ sourcecode/env_analyzer.py,sha256=YXlaxFBuf-ladWmb3iLCNMN-rKhP2JuqAIDwZdiIZHQ,18473
19
19
  sourcecode/file_classifier.py,sha256=_KfFIIolharaIxbSTrCkaWauQIqNHCyor_n47RGyDh8,8577
20
20
  sourcecode/git_analyzer.py,sha256=PD3eNWydznQ6KLNpxGzBqizIHoPIKevfwz9Xyf_pDt4,11600
21
21
  sourcecode/graph_analyzer.py,sha256=hMOsLLz9B0UnQ4xwbHdgr3bFvqpw0bQ8kN-xmEn3Krk,64156
@@ -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=ofEge9hTWHOTjeWt7ceCDQWzP-uhhenrYX2usjW2KVU,22759
31
31
  sourcecode/semantic_analyzer.py,sha256=16EFTgM7ooW0m5gNUKOlTSn7IEMLSzKmzQn-cWaSqjs,82604
32
- sourcecode/serializer.py,sha256=uDYSGjNjyrI2Qqvq23dl0owfi7zUVo8bwHBJ2RlGdz8,58975
32
+ sourcecode/serializer.py,sha256=CBK_i1joiNl8aCjFgnyIMrEFhAVCAk_ltfOqot2yVC4,59361
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=fQlVoNx8S-fSHpKoJ0JBvEHCFkxszH0KZVJed1i3TRk,6845
@@ -40,7 +40,7 @@ sourcecode/detectors/dart.py,sha256=QbqaL5v18-_ort75HihVBt8MsKUfOcFDF8IpWFLiXpI,
40
40
  sourcecode/detectors/dotnet.py,sha256=oi8zq3AfUItlK3h_qM81vOe1ZVTIU9LBKIlIrRDuqOs,6864
41
41
  sourcecode/detectors/elixir.py,sha256=jCpvt5Yi6jvplc80ovRtWh17q-11ZGo9qX7o8b57TJE,1713
42
42
  sourcecode/detectors/go.py,sha256=2r66uRQfeTWsqxr4HDhT6vExZErby0t46QXLHVBRv9w,2782
43
- sourcecode/detectors/heuristic.py,sha256=Hab_Uiuxtq-WBs_wCnzETBS5hhaxeEtf-GOGMH63cv0,2887
43
+ sourcecode/detectors/heuristic.py,sha256=bCqqgbHavl4Sse3dqT8mwmo1wAdgeJr7VyXOmfClLKo,3387
44
44
  sourcecode/detectors/hybrid.py,sha256=IGFRUVsAZ1ooRlFdznCeJAV6vy1yVDx-VyghvLtddXc,9101
45
45
  sourcecode/detectors/java.py,sha256=cZvB13cqJ76zHDncEG-TOCuK8gJjJN2mZGS2DGEcZy8,7715
46
46
  sourcecode/detectors/jvm_ext.py,sha256=EgHJ5W8EE-ZTN9V607mVzohyKgZE8Mc2jCi-DF8RAZU,2616
@@ -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-0.44.0.dist-info/METADATA,sha256=pxp0MxePWfJw419_NjcL3P8C2Rk4yjH9JqIKMyDuVqo,25209
64
- sourcecode-0.44.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
65
- sourcecode-0.44.0.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
66
- sourcecode-0.44.0.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
67
- sourcecode-0.44.0.dist-info/RECORD,,
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,,