sourcecode 0.48.0__tar.gz → 0.49.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.48.0 → sourcecode-0.49.0}/PKG-INFO +1 -1
  2. {sourcecode-0.48.0 → sourcecode-0.49.0}/pyproject.toml +1 -1
  3. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/__init__.py +1 -1
  4. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/cli.py +2 -0
  5. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/contract_pipeline.py +2 -2
  6. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/serializer.py +36 -32
  7. {sourcecode-0.48.0 → sourcecode-0.49.0}/.agents/skills/source-command-gsd-join-discord/SKILL.md +0 -0
  8. {sourcecode-0.48.0 → sourcecode-0.49.0}/.agents/skills/source-command-gsd-review-backlog/SKILL.md +0 -0
  9. {sourcecode-0.48.0 → sourcecode-0.49.0}/.agents/skills/source-command-gsd-workstreams/SKILL.md +0 -0
  10. {sourcecode-0.48.0 → sourcecode-0.49.0}/.gitignore +0 -0
  11. {sourcecode-0.48.0 → sourcecode-0.49.0}/.ruff.toml +0 -0
  12. {sourcecode-0.48.0 → sourcecode-0.49.0}/CONTRIBUTING.md +0 -0
  13. {sourcecode-0.48.0 → sourcecode-0.49.0}/LICENSE +0 -0
  14. {sourcecode-0.48.0 → sourcecode-0.49.0}/README.md +0 -0
  15. {sourcecode-0.48.0 → sourcecode-0.49.0}/SECURITY.md +0 -0
  16. {sourcecode-0.48.0 → sourcecode-0.49.0}/docs/privacy.md +0 -0
  17. {sourcecode-0.48.0 → sourcecode-0.49.0}/docs/schema.md +0 -0
  18. {sourcecode-0.48.0 → sourcecode-0.49.0}/raw +0 -0
  19. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/adaptive_scanner.py +0 -0
  20. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/architecture_analyzer.py +0 -0
  21. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/architecture_summary.py +0 -0
  22. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/ast_extractor.py +0 -0
  23. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/classifier.py +0 -0
  24. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/code_notes_analyzer.py +0 -0
  25. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/confidence_analyzer.py +0 -0
  26. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/context_scorer.py +0 -0
  27. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/context_summarizer.py +0 -0
  28. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/contract_model.py +0 -0
  29. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/coverage_parser.py +0 -0
  30. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/dependency_analyzer.py +0 -0
  31. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/detectors/__init__.py +0 -0
  32. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/detectors/base.py +0 -0
  33. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
  34. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/detectors/dart.py +0 -0
  35. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/detectors/dotnet.py +0 -0
  36. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/detectors/elixir.py +0 -0
  37. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/detectors/go.py +0 -0
  38. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/detectors/heuristic.py +0 -0
  39. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/detectors/hybrid.py +0 -0
  40. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/detectors/java.py +0 -0
  41. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
  42. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/detectors/nodejs.py +0 -0
  43. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/detectors/parsers.py +0 -0
  44. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/detectors/php.py +0 -0
  45. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/detectors/project.py +0 -0
  46. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/detectors/python.py +0 -0
  47. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/detectors/ruby.py +0 -0
  48. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/detectors/rust.py +0 -0
  49. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/detectors/systems.py +0 -0
  50. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/detectors/terraform.py +0 -0
  51. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/detectors/tooling.py +0 -0
  52. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/doc_analyzer.py +0 -0
  53. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/entrypoint_classifier.py +0 -0
  54. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/env_analyzer.py +0 -0
  55. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/file_classifier.py +0 -0
  56. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/git_analyzer.py +0 -0
  57. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/graph_analyzer.py +0 -0
  58. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/metrics_analyzer.py +0 -0
  59. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/prepare_context.py +0 -0
  60. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/ranking_engine.py +0 -0
  61. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/redactor.py +0 -0
  62. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/relevance_scorer.py +0 -0
  63. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/repo_classifier.py +0 -0
  64. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/runtime_classifier.py +0 -0
  65. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/scanner.py +0 -0
  66. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/schema.py +0 -0
  67. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/semantic_analyzer.py +0 -0
  68. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/summarizer.py +0 -0
  69. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/telemetry/__init__.py +0 -0
  70. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/telemetry/config.py +0 -0
  71. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/telemetry/consent.py +0 -0
  72. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/telemetry/events.py +0 -0
  73. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/telemetry/filters.py +0 -0
  74. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/telemetry/transport.py +0 -0
  75. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/tree_utils.py +0 -0
  76. {sourcecode-0.48.0 → sourcecode-0.49.0}/src/sourcecode/workspace.py +0 -0
  77. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/__init__.py +0 -0
  78. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/conftest.py +0 -0
  79. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/fixtures/coverage.xml +0 -0
  80. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/fixtures/fastapi_app/pyproject.toml +0 -0
  81. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/fixtures/fastapi_app/src/main.py +0 -0
  82. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/fixtures/go_service/cmd/api/main.go +0 -0
  83. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/fixtures/go_service/go.mod +0 -0
  84. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/fixtures/jacoco.xml +0 -0
  85. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/fixtures/lcov.info +0 -0
  86. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/fixtures/nextjs_app/app/page.tsx +0 -0
  87. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/fixtures/nextjs_app/package.json +0 -0
  88. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/fixtures/nextjs_app/pnpm-lock.yaml +0 -0
  89. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/fixtures/pnpm_monorepo/apps/web/app/page.tsx +0 -0
  90. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/fixtures/pnpm_monorepo/apps/web/package.json +0 -0
  91. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/fixtures/pnpm_monorepo/packages/api/main.py +0 -0
  92. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/fixtures/pnpm_monorepo/packages/api/pyproject.toml +0 -0
  93. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/fixtures/pnpm_monorepo/pnpm-workspace.yaml +0 -0
  94. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_architecture_analyzer.py +0 -0
  95. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_architecture_summary.py +0 -0
  96. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_ast_extractor.py +0 -0
  97. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_block1_reliability.py +0 -0
  98. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_block2_coverage.py +0 -0
  99. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_block5_quality.py +0 -0
  100. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_classifier.py +0 -0
  101. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_cli.py +0 -0
  102. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_code_notes_analyzer.py +0 -0
  103. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_context_scorer.py +0 -0
  104. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_contract_pipeline.py +0 -0
  105. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_coverage_parser.py +0 -0
  106. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_cross_consistency.py +0 -0
  107. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_dependency_analyzer_node_python.py +0 -0
  108. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_dependency_analyzer_polyglot.py +0 -0
  109. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_dependency_schema.py +0 -0
  110. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_detector_dotnet.py +0 -0
  111. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_detector_go_rust_java.py +0 -0
  112. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_detector_nodejs.py +0 -0
  113. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_detector_php_ruby_dart.py +0 -0
  114. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_detector_python.py +0 -0
  115. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_detector_universal_managed.py +0 -0
  116. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_detector_universal_systems.py +0 -0
  117. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_detectors_base.py +0 -0
  118. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_doc_analyzer_jsdom.py +0 -0
  119. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_doc_analyzer_python.py +0 -0
  120. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_graph_analyzer_polyglot.py +0 -0
  121. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_graph_analyzer_python_node.py +0 -0
  122. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_graph_schema.py +0 -0
  123. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_hybrid_inference.py +0 -0
  124. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_integration.py +0 -0
  125. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_integration_dependencies.py +0 -0
  126. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_integration_detection.py +0 -0
  127. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_integration_docs.py +0 -0
  128. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_integration_graph_modules.py +0 -0
  129. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_integration_lqn.py +0 -0
  130. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_integration_metrics.py +0 -0
  131. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_integration_multistack.py +0 -0
  132. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_integration_semantics.py +0 -0
  133. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_integration_universal.py +0 -0
  134. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_metrics_analyzer.py +0 -0
  135. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_packaging.py +0 -0
  136. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_phase1_improvements.py +0 -0
  137. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_pipeline_integrity.py +0 -0
  138. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_real_projects.py +0 -0
  139. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_redactor.py +0 -0
  140. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_scanner.py +0 -0
  141. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_schema.py +0 -0
  142. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_schema_normalization.py +0 -0
  143. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_semantic_analyzer_node.py +0 -0
  144. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_semantic_analyzer_python.py +0 -0
  145. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_semantic_import_resolution.py +0 -0
  146. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_semantic_schema.py +0 -0
  147. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_signal_hierarchy.py +0 -0
  148. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_summarizer.py +0 -0
  149. {sourcecode-0.48.0 → sourcecode-0.49.0}/tests/test_telemetry.py +0 -0
  150. {sourcecode-0.48.0 → sourcecode-0.49.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.48.0
3
+ Version: 0.49.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.48.0"
7
+ version = "0.49.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.48.0"
3
+ __version__ = "0.49.0"
@@ -1445,8 +1445,10 @@ def main(
1445
1445
  data = agent_view(sm)
1446
1446
  # When contract pipeline ran (mode=contract, no legacy flags), include
1447
1447
  # per-file contracts in agent output so agents get structural context.
1448
+ # Remove file_relevance — contracts cover this signal with more detail.
1448
1449
  if _is_contract_mode and sm.file_contracts:
1449
1450
  from sourcecode.serializer import _serialize_contract_minimal
1451
+ data.pop("file_relevance", None)
1450
1452
  data["contracts"] = [_serialize_contract_minimal(c) for c in sm.file_contracts]
1451
1453
  if sm.contract_summary is not None:
1452
1454
  cs = sm.contract_summary
@@ -27,8 +27,8 @@ from sourcecode.schema import EntryPoint, MonorepoPackageInfo
27
27
  # ---------------------------------------------------------------------------
28
28
 
29
29
  _MAX_FILES = 500 # hard cap on files extracted per run
30
- _MAX_CONTRACTS = 25 # default top-N output cap — omit rather than flood
31
- _MIN_CONTRACT_SCORE = 0.10 # drop contracts below this relevance threshold
30
+ _MAX_CONTRACTS = 15 # default top-N output cap — omit rather than flood
31
+ _MIN_CONTRACT_SCORE = 0.15 # drop contracts below this relevance threshold
32
32
  _SRC_EXTENSIONS: frozenset[str] = frozenset(_LANGUAGE_MAP.keys())
33
33
 
34
34
 
@@ -1130,9 +1130,6 @@ def _contract_view_minimal(
1130
1130
  "project": project,
1131
1131
  }
1132
1132
 
1133
- if sm.metadata.traversal_topology:
1134
- result["traversal"] = sm.metadata.traversal_topology
1135
-
1136
1133
  # Per-file contracts
1137
1134
  if contracts:
1138
1135
  serialized: list[dict[str, Any]] = []
@@ -1310,6 +1307,10 @@ def _compress_sig(name: str, sig: str, max_len: int = 100) -> str:
1310
1307
  return full
1311
1308
 
1312
1309
 
1310
+ _MAX_FN_PER_CONTRACT = 5 # max function signatures per contract (token budget)
1311
+ _MAX_SIG_LEN = 60 # max chars per compressed signature
1312
+
1313
+
1313
1314
  def _serialize_contract_minimal(c: Any) -> dict[str, Any]:
1314
1315
  """Serialize one FileContract to minimal format."""
1315
1316
  item: dict[str, Any] = {"path": c.path, "role": c.role}
@@ -1317,38 +1318,47 @@ def _serialize_contract_minimal(c: Any) -> dict[str, Any]:
1317
1318
  if c.is_changed:
1318
1319
  item["changed"] = True
1319
1320
 
1320
- # Exports: flat string for functions/unknown, {name,k} for others
1321
- # When all exports are same non-function kind, group them
1322
- if c.exports:
1323
- exs: list[Any] = []
1324
- kinds = {e.kind for e in c.exports}
1325
- if len(kinds) == 1 and "function" not in kinds and "unknown" not in kinds:
1326
- # All same non-function kind — compact: {"k": "class", "names": [...]}
1327
- only_kind = next(iter(kinds))
1328
- exs = [{"k": only_kind, "names": sorted(e.name for e in c.exports)}]
1329
- else:
1330
- for e in sorted(c.exports, key=lambda e: e.name):
1331
- if e.kind in ("function", "unknown"):
1332
- exs.append(e.name)
1333
- else:
1334
- exs.append({"name": e.name, "k": e.kind})
1335
- item["exports"] = exs
1336
-
1337
- # External deps (non-stdlib already filtered in extractor)
1338
- if c.dependencies:
1339
- item["deps"] = sorted(c.dependencies)
1340
-
1341
- # Exported function signatures — compressed
1321
+ # Exported function signatures compressed, capped
1342
1322
  exported_names = {e.name for e in c.exports}
1323
+ fn_names_in_sigs: set[str] = set()
1343
1324
  if c.functions:
1344
1325
  fns = []
1345
1326
  for f in sorted(c.functions, key=lambda f: f.name):
1346
1327
  if not (f.exported or f.name in exported_names):
1347
1328
  continue
1348
- fns.append(_compress_sig(f.name, f.signature))
1329
+ fns.append(_compress_sig(f.name, f.signature, max_len=_MAX_SIG_LEN))
1330
+ fn_names_in_sigs.add(f.name)
1331
+ if len(fns) >= _MAX_FN_PER_CONTRACT:
1332
+ break
1349
1333
  if fns:
1350
1334
  item["fn"] = fns
1351
1335
 
1336
+ # Exports: omit function names already shown in fn; keep non-function exports
1337
+ if c.exports:
1338
+ exs: list[Any] = []
1339
+ non_fn_exports = [e for e in c.exports if e.kind not in ("function", "unknown")]
1340
+ fn_exports_not_in_sig = [
1341
+ e for e in c.exports
1342
+ if e.kind in ("function", "unknown") and e.name not in fn_names_in_sigs
1343
+ ]
1344
+ remaining = non_fn_exports + fn_exports_not_in_sig
1345
+ if remaining:
1346
+ kinds = {e.kind for e in remaining}
1347
+ if len(kinds) == 1 and "function" not in kinds and "unknown" not in kinds:
1348
+ only_kind = next(iter(kinds))
1349
+ exs = [{"k": only_kind, "names": sorted(e.name for e in remaining)}]
1350
+ else:
1351
+ for e in sorted(remaining, key=lambda e: e.name):
1352
+ if e.kind in ("function", "unknown"):
1353
+ exs.append(e.name)
1354
+ else:
1355
+ exs.append({"name": e.name, "k": e.kind})
1356
+ item["exports"] = exs
1357
+
1358
+ # External deps (non-stdlib already filtered in extractor)
1359
+ if c.dependencies:
1360
+ item["deps"] = sorted(c.dependencies)
1361
+
1352
1362
  # Types: skip if fully covered by exports (avoids duplication in model files)
1353
1363
  if c.types:
1354
1364
  export_names_set = {e.name for e in c.exports}
@@ -1363,12 +1373,6 @@ def _serialize_contract_minimal(c: Any) -> dict[str, Any]:
1363
1373
  if c.hooks_used:
1364
1374
  item["hooks"] = c.hooks_used
1365
1375
 
1366
- # Ranking signals: why this file was ranked here
1367
- if getattr(c, "ranking_reasons", None):
1368
- non_trivial = [r for r in c.ranking_reasons if r not in ("source file", "noise")]
1369
- if non_trivial:
1370
- item["why"] = non_trivial
1371
-
1372
1376
  return item
1373
1377
 
1374
1378
 
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