sourcecode 0.44.0__tar.gz → 0.45.0__tar.gz

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.
Files changed (150) hide show
  1. {sourcecode-0.44.0 → sourcecode-0.45.0}/PKG-INFO +1 -1
  2. {sourcecode-0.44.0 → sourcecode-0.45.0}/pyproject.toml +1 -1
  3. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/__init__.py +1 -1
  4. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/cli.py +71 -24
  5. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/contract_pipeline.py +3 -1
  6. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/detectors/heuristic.py +9 -1
  7. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/env_analyzer.py +2 -2
  8. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/serializer.py +9 -3
  9. {sourcecode-0.44.0 → sourcecode-0.45.0}/.agents/skills/source-command-gsd-join-discord/SKILL.md +0 -0
  10. {sourcecode-0.44.0 → sourcecode-0.45.0}/.agents/skills/source-command-gsd-review-backlog/SKILL.md +0 -0
  11. {sourcecode-0.44.0 → sourcecode-0.45.0}/.agents/skills/source-command-gsd-workstreams/SKILL.md +0 -0
  12. {sourcecode-0.44.0 → sourcecode-0.45.0}/.gitignore +0 -0
  13. {sourcecode-0.44.0 → sourcecode-0.45.0}/.ruff.toml +0 -0
  14. {sourcecode-0.44.0 → sourcecode-0.45.0}/CONTRIBUTING.md +0 -0
  15. {sourcecode-0.44.0 → sourcecode-0.45.0}/LICENSE +0 -0
  16. {sourcecode-0.44.0 → sourcecode-0.45.0}/README.md +0 -0
  17. {sourcecode-0.44.0 → sourcecode-0.45.0}/SECURITY.md +0 -0
  18. {sourcecode-0.44.0 → sourcecode-0.45.0}/docs/privacy.md +0 -0
  19. {sourcecode-0.44.0 → sourcecode-0.45.0}/docs/schema.md +0 -0
  20. {sourcecode-0.44.0 → sourcecode-0.45.0}/raw +0 -0
  21. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/adaptive_scanner.py +0 -0
  22. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/architecture_analyzer.py +0 -0
  23. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/architecture_summary.py +0 -0
  24. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/ast_extractor.py +0 -0
  25. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/classifier.py +0 -0
  26. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/code_notes_analyzer.py +0 -0
  27. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/confidence_analyzer.py +0 -0
  28. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/context_scorer.py +0 -0
  29. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/context_summarizer.py +0 -0
  30. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/contract_model.py +0 -0
  31. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/coverage_parser.py +0 -0
  32. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/dependency_analyzer.py +0 -0
  33. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/detectors/__init__.py +0 -0
  34. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/detectors/base.py +0 -0
  35. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
  36. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/detectors/dart.py +0 -0
  37. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/detectors/dotnet.py +0 -0
  38. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/detectors/elixir.py +0 -0
  39. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/detectors/go.py +0 -0
  40. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/detectors/hybrid.py +0 -0
  41. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/detectors/java.py +0 -0
  42. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
  43. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/detectors/nodejs.py +0 -0
  44. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/detectors/parsers.py +0 -0
  45. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/detectors/php.py +0 -0
  46. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/detectors/project.py +0 -0
  47. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/detectors/python.py +0 -0
  48. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/detectors/ruby.py +0 -0
  49. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/detectors/rust.py +0 -0
  50. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/detectors/systems.py +0 -0
  51. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/detectors/terraform.py +0 -0
  52. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/detectors/tooling.py +0 -0
  53. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/doc_analyzer.py +0 -0
  54. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/entrypoint_classifier.py +0 -0
  55. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/file_classifier.py +0 -0
  56. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/git_analyzer.py +0 -0
  57. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/graph_analyzer.py +0 -0
  58. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/metrics_analyzer.py +0 -0
  59. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/prepare_context.py +0 -0
  60. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/ranking_engine.py +0 -0
  61. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/redactor.py +0 -0
  62. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/relevance_scorer.py +0 -0
  63. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/repo_classifier.py +0 -0
  64. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/runtime_classifier.py +0 -0
  65. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/scanner.py +0 -0
  66. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/schema.py +0 -0
  67. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/semantic_analyzer.py +0 -0
  68. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/summarizer.py +0 -0
  69. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/telemetry/__init__.py +0 -0
  70. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/telemetry/config.py +0 -0
  71. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/telemetry/consent.py +0 -0
  72. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/telemetry/events.py +0 -0
  73. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/telemetry/filters.py +0 -0
  74. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/telemetry/transport.py +0 -0
  75. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/tree_utils.py +0 -0
  76. {sourcecode-0.44.0 → sourcecode-0.45.0}/src/sourcecode/workspace.py +0 -0
  77. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/__init__.py +0 -0
  78. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/conftest.py +0 -0
  79. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/fixtures/coverage.xml +0 -0
  80. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/fixtures/fastapi_app/pyproject.toml +0 -0
  81. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/fixtures/fastapi_app/src/main.py +0 -0
  82. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/fixtures/go_service/cmd/api/main.go +0 -0
  83. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/fixtures/go_service/go.mod +0 -0
  84. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/fixtures/jacoco.xml +0 -0
  85. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/fixtures/lcov.info +0 -0
  86. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/fixtures/nextjs_app/app/page.tsx +0 -0
  87. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/fixtures/nextjs_app/package.json +0 -0
  88. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/fixtures/nextjs_app/pnpm-lock.yaml +0 -0
  89. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/fixtures/pnpm_monorepo/apps/web/app/page.tsx +0 -0
  90. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/fixtures/pnpm_monorepo/apps/web/package.json +0 -0
  91. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/fixtures/pnpm_monorepo/packages/api/main.py +0 -0
  92. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/fixtures/pnpm_monorepo/packages/api/pyproject.toml +0 -0
  93. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/fixtures/pnpm_monorepo/pnpm-workspace.yaml +0 -0
  94. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_architecture_analyzer.py +0 -0
  95. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_architecture_summary.py +0 -0
  96. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_ast_extractor.py +0 -0
  97. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_block1_reliability.py +0 -0
  98. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_block2_coverage.py +0 -0
  99. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_block5_quality.py +0 -0
  100. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_classifier.py +0 -0
  101. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_cli.py +0 -0
  102. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_code_notes_analyzer.py +0 -0
  103. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_context_scorer.py +0 -0
  104. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_contract_pipeline.py +0 -0
  105. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_coverage_parser.py +0 -0
  106. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_cross_consistency.py +0 -0
  107. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_dependency_analyzer_node_python.py +0 -0
  108. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_dependency_analyzer_polyglot.py +0 -0
  109. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_dependency_schema.py +0 -0
  110. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_detector_dotnet.py +0 -0
  111. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_detector_go_rust_java.py +0 -0
  112. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_detector_nodejs.py +0 -0
  113. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_detector_php_ruby_dart.py +0 -0
  114. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_detector_python.py +0 -0
  115. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_detector_universal_managed.py +0 -0
  116. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_detector_universal_systems.py +0 -0
  117. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_detectors_base.py +0 -0
  118. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_doc_analyzer_jsdom.py +0 -0
  119. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_doc_analyzer_python.py +0 -0
  120. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_graph_analyzer_polyglot.py +0 -0
  121. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_graph_analyzer_python_node.py +0 -0
  122. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_graph_schema.py +0 -0
  123. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_hybrid_inference.py +0 -0
  124. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_integration.py +0 -0
  125. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_integration_dependencies.py +0 -0
  126. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_integration_detection.py +0 -0
  127. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_integration_docs.py +0 -0
  128. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_integration_graph_modules.py +0 -0
  129. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_integration_lqn.py +0 -0
  130. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_integration_metrics.py +0 -0
  131. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_integration_multistack.py +0 -0
  132. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_integration_semantics.py +0 -0
  133. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_integration_universal.py +0 -0
  134. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_metrics_analyzer.py +0 -0
  135. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_packaging.py +0 -0
  136. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_phase1_improvements.py +0 -0
  137. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_pipeline_integrity.py +0 -0
  138. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_real_projects.py +0 -0
  139. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_redactor.py +0 -0
  140. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_scanner.py +0 -0
  141. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_schema.py +0 -0
  142. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_schema_normalization.py +0 -0
  143. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_semantic_analyzer_node.py +0 -0
  144. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_semantic_analyzer_python.py +0 -0
  145. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_semantic_import_resolution.py +0 -0
  146. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_semantic_schema.py +0 -0
  147. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_signal_hierarchy.py +0 -0
  148. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_summarizer.py +0 -0
  149. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_telemetry.py +0 -0
  150. {sourcecode-0.44.0 → sourcecode-0.45.0}/tests/test_workspace_analyzer.py +0 -0
@@ -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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sourcecode"
7
- version = "0.44.0"
7
+ version = "0.45.0"
8
8
  description = "Deterministic codebase context for AI coding agents"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -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"
@@ -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 = {
@@ -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:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes