sourcecode 0.36.0__tar.gz → 0.37.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 (142) hide show
  1. {sourcecode-0.36.0 → sourcecode-0.37.0}/PKG-INFO +1 -1
  2. {sourcecode-0.36.0 → sourcecode-0.37.0}/pyproject.toml +1 -1
  3. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/__init__.py +1 -1
  4. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/cli.py +66 -17
  5. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/git_analyzer.py +7 -0
  6. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/metrics_analyzer.py +10 -0
  7. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/serializer.py +20 -1
  8. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_contract_pipeline.py +2 -2
  9. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_integration.py +1 -1
  10. {sourcecode-0.36.0 → sourcecode-0.37.0}/.agents/skills/source-command-gsd-join-discord/SKILL.md +0 -0
  11. {sourcecode-0.36.0 → sourcecode-0.37.0}/.agents/skills/source-command-gsd-review-backlog/SKILL.md +0 -0
  12. {sourcecode-0.36.0 → sourcecode-0.37.0}/.agents/skills/source-command-gsd-workstreams/SKILL.md +0 -0
  13. {sourcecode-0.36.0 → sourcecode-0.37.0}/.gitignore +0 -0
  14. {sourcecode-0.36.0 → sourcecode-0.37.0}/.ruff.toml +0 -0
  15. {sourcecode-0.36.0 → sourcecode-0.37.0}/CONTRIBUTING.md +0 -0
  16. {sourcecode-0.36.0 → sourcecode-0.37.0}/LICENSE +0 -0
  17. {sourcecode-0.36.0 → sourcecode-0.37.0}/README.md +0 -0
  18. {sourcecode-0.36.0 → sourcecode-0.37.0}/SECURITY.md +0 -0
  19. {sourcecode-0.36.0 → sourcecode-0.37.0}/docs/privacy.md +0 -0
  20. {sourcecode-0.36.0 → sourcecode-0.37.0}/docs/schema.md +0 -0
  21. {sourcecode-0.36.0 → sourcecode-0.37.0}/raw +0 -0
  22. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/architecture_analyzer.py +0 -0
  23. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/architecture_summary.py +0 -0
  24. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/ast_extractor.py +0 -0
  25. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/classifier.py +0 -0
  26. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/code_notes_analyzer.py +0 -0
  27. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/confidence_analyzer.py +0 -0
  28. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/context_summarizer.py +0 -0
  29. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/contract_model.py +0 -0
  30. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/contract_pipeline.py +0 -0
  31. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/coverage_parser.py +0 -0
  32. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/dependency_analyzer.py +0 -0
  33. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/detectors/__init__.py +0 -0
  34. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/detectors/base.py +0 -0
  35. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
  36. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/detectors/dart.py +0 -0
  37. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/detectors/dotnet.py +0 -0
  38. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/detectors/elixir.py +0 -0
  39. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/detectors/go.py +0 -0
  40. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/detectors/heuristic.py +0 -0
  41. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/detectors/hybrid.py +0 -0
  42. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/detectors/java.py +0 -0
  43. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
  44. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/detectors/nodejs.py +0 -0
  45. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/detectors/parsers.py +0 -0
  46. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/detectors/php.py +0 -0
  47. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/detectors/project.py +0 -0
  48. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/detectors/python.py +0 -0
  49. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/detectors/ruby.py +0 -0
  50. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/detectors/rust.py +0 -0
  51. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/detectors/systems.py +0 -0
  52. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/detectors/terraform.py +0 -0
  53. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/detectors/tooling.py +0 -0
  54. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/doc_analyzer.py +0 -0
  55. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/entrypoint_classifier.py +0 -0
  56. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/env_analyzer.py +0 -0
  57. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/file_classifier.py +0 -0
  58. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/graph_analyzer.py +0 -0
  59. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/prepare_context.py +0 -0
  60. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/redactor.py +0 -0
  61. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/relevance_scorer.py +0 -0
  62. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/runtime_classifier.py +0 -0
  63. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/scanner.py +0 -0
  64. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/schema.py +0 -0
  65. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/semantic_analyzer.py +0 -0
  66. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/summarizer.py +0 -0
  67. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/telemetry/__init__.py +0 -0
  68. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/telemetry/config.py +0 -0
  69. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/telemetry/consent.py +0 -0
  70. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/telemetry/events.py +0 -0
  71. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/telemetry/filters.py +0 -0
  72. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/telemetry/transport.py +0 -0
  73. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/tree_utils.py +0 -0
  74. {sourcecode-0.36.0 → sourcecode-0.37.0}/src/sourcecode/workspace.py +0 -0
  75. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/__init__.py +0 -0
  76. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/conftest.py +0 -0
  77. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/fixtures/coverage.xml +0 -0
  78. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/fixtures/fastapi_app/pyproject.toml +0 -0
  79. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/fixtures/fastapi_app/src/main.py +0 -0
  80. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/fixtures/go_service/cmd/api/main.go +0 -0
  81. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/fixtures/go_service/go.mod +0 -0
  82. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/fixtures/jacoco.xml +0 -0
  83. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/fixtures/lcov.info +0 -0
  84. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/fixtures/nextjs_app/app/page.tsx +0 -0
  85. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/fixtures/nextjs_app/package.json +0 -0
  86. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/fixtures/nextjs_app/pnpm-lock.yaml +0 -0
  87. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/fixtures/pnpm_monorepo/apps/web/app/page.tsx +0 -0
  88. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/fixtures/pnpm_monorepo/apps/web/package.json +0 -0
  89. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/fixtures/pnpm_monorepo/packages/api/main.py +0 -0
  90. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/fixtures/pnpm_monorepo/packages/api/pyproject.toml +0 -0
  91. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/fixtures/pnpm_monorepo/pnpm-workspace.yaml +0 -0
  92. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_architecture_analyzer.py +0 -0
  93. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_architecture_summary.py +0 -0
  94. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_ast_extractor.py +0 -0
  95. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_classifier.py +0 -0
  96. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_cli.py +0 -0
  97. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_code_notes_analyzer.py +0 -0
  98. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_coverage_parser.py +0 -0
  99. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_cross_consistency.py +0 -0
  100. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_dependency_analyzer_node_python.py +0 -0
  101. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_dependency_analyzer_polyglot.py +0 -0
  102. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_dependency_schema.py +0 -0
  103. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_detector_dotnet.py +0 -0
  104. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_detector_go_rust_java.py +0 -0
  105. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_detector_nodejs.py +0 -0
  106. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_detector_php_ruby_dart.py +0 -0
  107. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_detector_python.py +0 -0
  108. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_detector_universal_managed.py +0 -0
  109. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_detector_universal_systems.py +0 -0
  110. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_detectors_base.py +0 -0
  111. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_doc_analyzer_jsdom.py +0 -0
  112. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_doc_analyzer_python.py +0 -0
  113. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_graph_analyzer_polyglot.py +0 -0
  114. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_graph_analyzer_python_node.py +0 -0
  115. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_graph_schema.py +0 -0
  116. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_hybrid_inference.py +0 -0
  117. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_integration_dependencies.py +0 -0
  118. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_integration_detection.py +0 -0
  119. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_integration_docs.py +0 -0
  120. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_integration_graph_modules.py +0 -0
  121. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_integration_lqn.py +0 -0
  122. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_integration_metrics.py +0 -0
  123. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_integration_multistack.py +0 -0
  124. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_integration_semantics.py +0 -0
  125. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_integration_universal.py +0 -0
  126. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_metrics_analyzer.py +0 -0
  127. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_packaging.py +0 -0
  128. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_phase1_improvements.py +0 -0
  129. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_pipeline_integrity.py +0 -0
  130. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_real_projects.py +0 -0
  131. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_redactor.py +0 -0
  132. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_scanner.py +0 -0
  133. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_schema.py +0 -0
  134. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_schema_normalization.py +0 -0
  135. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_semantic_analyzer_node.py +0 -0
  136. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_semantic_analyzer_python.py +0 -0
  137. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_semantic_import_resolution.py +0 -0
  138. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_semantic_schema.py +0 -0
  139. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_signal_hierarchy.py +0 -0
  140. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_summarizer.py +0 -0
  141. {sourcecode-0.36.0 → sourcecode-0.37.0}/tests/test_telemetry.py +0 -0
  142. {sourcecode-0.36.0 → sourcecode-0.37.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.36.0
3
+ Version: 0.37.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.36.0"
7
+ version = "0.37.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.36.0"
3
+ __version__ = "0.37.0"
@@ -384,7 +384,8 @@ def main(
384
384
  no_tree: bool = typer.Option(
385
385
  False,
386
386
  "--no-tree",
387
- help="(Deprecated) Previously suppressed file_tree. The file tree is excluded by default — this flag is now a no-op. Use --tree to include the file tree.",
387
+ hidden=True,
388
+ help="(Removed) No-op. File tree is excluded by default. Use --tree to include it.",
388
389
  ),
389
390
  tree: bool = typer.Option(
390
391
  False,
@@ -516,13 +517,13 @@ def main(
516
517
  "contract",
517
518
  "--mode",
518
519
  help=(
519
- "Output mode: contract|minimal (default) | standard | deep | hybrid | raw. "
520
- "contract/minimal: minimal per-file contracts — exports, signatures, deps. Smallest output. "
520
+ "Output mode: contract (default) | standard | raw. "
521
+ "contract: minimal per-file contracts — exports, signatures, deps. "
522
+ "Smallest output, recommended for AI agents. "
523
+ "minimal is accepted as an alias for contract. "
521
524
  "standard: full per-file detail with imports, relevance scores, extraction method. "
522
- "deep: standard + optional analysis sections (deps, env, git). "
523
- "hybrid: contracts + compact bodies for top-ranked files. "
524
- "raw: legacy project-level analysis (stacks, entry points, dependencies). "
525
- "contract/minimal is the recommended default for AI coding agents."
525
+ "raw: project-level analysis only (stacks, entry points, dependency summary). "
526
+ "No per-file contracts."
526
527
  ),
527
528
  ),
528
529
  max_symbols: Optional[int] = typer.Option(
@@ -534,7 +535,8 @@ def main(
534
535
  dependency_depth: int = typer.Option(
535
536
  0,
536
537
  "--dependency-depth",
537
- help="Transitive import traversal depth for contract mode (0 = direct only, N = follow N levels).",
538
+ hidden=True,
539
+ help="(Removed) Transitive resolution is not implemented. Pass 0 or omit.",
538
540
  min=0,
539
541
  max=5,
540
542
  ),
@@ -561,7 +563,8 @@ def main(
561
563
  compress_types: bool = typer.Option(
562
564
  False,
563
565
  "--compress-types",
564
- help="Contract mode: abbreviate verbose type signatures (React.FC → FC, Promise<X> stays).",
566
+ hidden=True,
567
+ help="(Removed) No observable effect when type signatures are not extracted. Omit.",
565
568
  ),
566
569
  symbol: Optional[str] = typer.Option(
567
570
  None,
@@ -589,8 +592,20 @@ def main(
589
592
  _t0 = time.monotonic()
590
593
 
591
594
  # Validate new flag choices
592
- _MODE_CHOICES = ("contract", "minimal", "standard", "deep", "hybrid", "raw")
593
- if mode not in _MODE_CHOICES:
595
+ _MODE_CHOICES = ("contract", "minimal", "standard", "raw")
596
+ _DEPRECATED_MODES: dict[str, str] = {
597
+ "hybrid": "contract",
598
+ "deep": "standard",
599
+ }
600
+ if mode in _DEPRECATED_MODES:
601
+ fallback = _DEPRECATED_MODES[mode]
602
+ typer.echo(
603
+ f"[deprecated] --mode {mode} is removed: produced identical output to --mode {fallback}. "
604
+ f"Using --mode {fallback}.",
605
+ err=True,
606
+ )
607
+ mode = fallback
608
+ elif mode not in _MODE_CHOICES:
594
609
  typer.echo(
595
610
  f"Error: invalid value '{mode}' for --mode. Valid options: {', '.join(_MODE_CHOICES)}",
596
611
  err=True,
@@ -604,6 +619,22 @@ def main(
604
619
  )
605
620
  raise typer.Exit(code=1)
606
621
 
622
+ if dependency_depth > 0:
623
+ typer.echo(
624
+ f"[warning] --dependency-depth {dependency_depth} has no effect: "
625
+ "transitive import resolution is not implemented for npm/yarn/pip projects. "
626
+ "Using depth=0 (direct dependencies only).",
627
+ err=True,
628
+ )
629
+ dependency_depth = 0
630
+
631
+ if compress_types:
632
+ typer.echo(
633
+ "[deprecated] --compress-types is removed: type signatures are rarely extracted "
634
+ "at default depth. Flag ignored.",
635
+ err=True,
636
+ )
637
+
607
638
  # Validate format choices
608
639
  if format not in FORMAT_CHOICES:
609
640
  typer.echo(
@@ -634,9 +665,9 @@ def main(
634
665
  raise typer.Exit(code=1)
635
666
 
636
667
  # Normalize mode aliases
637
- _CONTRACT_MODES = frozenset({"contract", "minimal", "standard", "deep", "hybrid"})
668
+ _CONTRACT_MODES = frozenset({"contract", "minimal", "standard"})
638
669
  if mode == "minimal":
639
- mode = "contract" # minimal is the canonical default contract rendering
670
+ mode = "contract" # minimal is a documented alias for contract
640
671
  elif mode not in _CONTRACT_MODES and mode != "raw":
641
672
  mode = "contract" # unknown → safe default
642
673
 
@@ -648,15 +679,13 @@ def main(
648
679
  compact or agent or tree or format == "yaml" or trace_pipeline
649
680
  or docs or semantics or graph_modules or full_metrics or architecture
650
681
  )
651
- if mode in ("contract", "standard", "deep") and _legacy_flags_active:
682
+ if mode in ("contract", "standard") and _legacy_flags_active:
652
683
  mode = "raw"
653
684
 
654
685
  # Map mode to contract_view depth
655
686
  _CONTRACT_DEPTH = {
656
687
  "contract": "minimal",
657
688
  "standard": "standard",
658
- "deep": "deep",
659
- "hybrid": "minimal", # hybrid adds bodies via pipeline, minimal header
660
689
  }
661
690
 
662
691
  # --- Import analysis modules ---
@@ -746,6 +775,19 @@ def main(
746
775
  detector = ProjectDetector(build_default_detectors())
747
776
  workspace_analysis = WorkspaceAnalyzer().analyze(target, manifests)
748
777
 
778
+ # Warn when scanning a monorepo at default depth — typical package sources
779
+ # (packages/*/src/) live at depth 5+, so default depth=4 silently misses them.
780
+ # Only emit to TTY to avoid contaminating piped/CI output; agents read analysis_gaps.
781
+ import sys as _sys
782
+ if workspace_analysis.is_monorepo and depth <= 4 and effective_depth <= 4:
783
+ if _sys.stderr.isatty():
784
+ typer.echo(
785
+ f"[warning] monorepo detected with --depth {depth}. "
786
+ "Source files in packages/*/src/ (depth 5+) may be invisible. "
787
+ "Use --depth 6 or higher for full coverage.",
788
+ err=True,
789
+ )
790
+
749
791
  # --compact implicitly enables lightweight analysis passes so that
750
792
  # dependency_summary, env_summary and code_notes_summary are never null.
751
793
  if compact:
@@ -1244,7 +1286,7 @@ def main(
1244
1286
  sm = _replace(sm, pipeline_trace=_trace.build_trace())
1245
1287
 
1246
1288
  # Contract pipeline — runs for mode=contract|standard|deep|hybrid (skip for raw)
1247
- _is_contract_mode = mode in ("contract", "standard", "deep", "hybrid")
1289
+ _is_contract_mode = mode in ("contract", "standard")
1248
1290
  if _is_contract_mode:
1249
1291
  from sourcecode.contract_pipeline import ContractPipeline
1250
1292
  _cp = ContractPipeline()
@@ -1263,6 +1305,13 @@ def main(
1263
1305
  compress_types=compress_types,
1264
1306
  )
1265
1307
  sm = _replace(sm, file_contracts=_contracts, contract_summary=_contract_summary)
1308
+ if symbol is not None and len(_contracts) == 0:
1309
+ typer.echo(
1310
+ f"[warning] --symbol '{symbol}' matched 0 files. "
1311
+ "The symbol may not exist at the current --depth, or the name may differ in case. "
1312
+ "Try --depth 8 or verify the symbol name.",
1313
+ err=True,
1314
+ )
1266
1315
  if agent:
1267
1316
  typer.echo(f"[contract] {len(_contracts)} files extracted ({_contract_summary.method_breakdown})", err=True)
1268
1317
 
@@ -198,6 +198,13 @@ def _is_hotspot_admin(path: str) -> bool:
198
198
  for suffix in _HOTSPOT_ADMIN_SUFFIXES:
199
199
  if filename.endswith(suffix):
200
200
  return True
201
+ # Localized changelogs: CHANGELOG.zh-CN.md, CHANGES.en-US.md, etc.
202
+ _lower = filename.lower()
203
+ if _lower.startswith("changelog.") or _lower.startswith("changes."):
204
+ return True
205
+ # lerna.json and root-level package.json are modified by version bumps, not dev work
206
+ if filename in ("lerna.json",):
207
+ return True
201
208
  return False
202
209
 
203
210
 
@@ -219,6 +219,16 @@ class MetricsAnalyzer:
219
219
  if fm.language != "unknown":
220
220
  languages.add(fm.language)
221
221
 
222
+ # Emit explicit limitation when JS/TS files are present but complexity is unavailable.
223
+ # This prevents agents from assuming null complexity means "no functions found".
224
+ _js_ts_count = sum(1 for r in records if r.language in ("javascript", "typescript") and r.complexity_availability == "unavailable")
225
+ if _js_ts_count > 0:
226
+ limitations.append(
227
+ f"cyclomatic_complexity_unavailable: {_js_ts_count} JS/TS file(s) — "
228
+ "complexity requires tree-sitter (pip install 'sourcecode[ast]'). "
229
+ "null complexity fields are expected, not an error."
230
+ )
231
+
222
232
  summary = MetricsSummary(
223
233
  requested=True,
224
234
  file_count=len(records),
@@ -923,7 +923,7 @@ def _contract_view_minimal(
923
923
 
924
924
  result: dict[str, Any] = {
925
925
  "schema_version": sm.metadata.schema_version,
926
- "mode": "minimal",
926
+ "mode": "contract",
927
927
  "project": project,
928
928
  }
929
929
 
@@ -949,9 +949,28 @@ def _contract_view_minimal(
949
949
 
950
950
  if sm.env_summary is not None and sm.env_summary.requested:
951
951
  result["env_summary"] = asdict(sm.env_summary)
952
+ if sm.env_map:
953
+ # Include top-20 env entries sorted by required first, then name.
954
+ # Agents read the summary count but need the actual keys to act on them.
955
+ _sorted_env = sorted(sm.env_map, key=lambda e: (not getattr(e, "required", False), getattr(e, "name", "")))
956
+ result["env_map"] = [
957
+ {k: v for k, v in asdict(e).items() if v is not None and v != ""}
958
+ for e in _sorted_env[:20]
959
+ ]
952
960
 
953
961
  if sm.code_notes_summary is not None and sm.code_notes_summary.requested:
954
962
  result["code_notes_summary"] = asdict(sm.code_notes_summary)
963
+ if sm.code_notes:
964
+ # Include top-20 notes by severity: BUG > FIXME > DEPRECATED > TODO > others.
965
+ _SEVERITY_ORDER = {"BUG": 0, "FIXME": 1, "DEPRECATED": 2, "TODO": 3, "HACK": 4, "WARNING": 5}
966
+ _sorted_notes = sorted(
967
+ sm.code_notes,
968
+ key=lambda n: (_SEVERITY_ORDER.get(getattr(n, "kind", "").upper(), 9), getattr(n, "path", "")),
969
+ )
970
+ result["code_notes"] = [
971
+ {k: v for k, v in asdict(n).items() if v is not None and v != ""}
972
+ for n in _sorted_notes[:20]
973
+ ]
955
974
 
956
975
  if sm.git_context is not None and sm.git_context.requested:
957
976
  result["git_context"] = asdict(sm.git_context)
@@ -160,7 +160,7 @@ def test_cli_contract_mode_default(tmp_path: Path) -> None:
160
160
  assert result.exit_code == 0, result.output
161
161
  data = json.loads(result.output)
162
162
  # Default contract mode renders as minimal
163
- assert data["mode"] == "minimal"
163
+ assert data["mode"] == "contract"
164
164
  assert "contracts" in data
165
165
  assert "project" in data
166
166
  assert "schema_version" in data
@@ -174,7 +174,7 @@ def test_cli_contract_mode_explicit(tmp_path: Path) -> None:
174
174
  assert result.exit_code == 0, result.output
175
175
  data = json.loads(result.output)
176
176
  # --mode contract is an alias for minimal
177
- assert data["mode"] == "minimal"
177
+ assert data["mode"] == "contract"
178
178
  assert isinstance(data["contracts"], list)
179
179
 
180
180
 
@@ -31,7 +31,7 @@ def test_full_json_output(tmp_project: Path):
31
31
  data = json.loads(result.output)
32
32
  # Default mode is minimal contract — compact header + per-file contracts
33
33
  assert data["schema_version"] == "1.0"
34
- assert data["mode"] == "minimal"
34
+ assert data["mode"] == "contract"
35
35
  assert "file_tree" not in data
36
36
  assert data["project"]["type"] == "cli"
37
37
  assert "contracts" in data
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