sourcecode 1.42.0__tar.gz → 1.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 (120) hide show
  1. {sourcecode-1.42.0 → sourcecode-1.45.0}/CHANGELOG.md +69 -0
  2. {sourcecode-1.42.0 → sourcecode-1.45.0}/PKG-INFO +3 -3
  3. {sourcecode-1.42.0 → sourcecode-1.45.0}/README.md +2 -2
  4. {sourcecode-1.42.0 → sourcecode-1.45.0}/pyproject.toml +1 -1
  5. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/__init__.py +1 -1
  6. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/cli.py +57 -2
  7. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/mcp/orchestrator.py +23 -0
  8. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/repository_ir.py +26 -3
  9. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/validation_surface.py +15 -0
  10. {sourcecode-1.42.0 → sourcecode-1.45.0}/.github/workflows/build-windows.yml +0 -0
  11. {sourcecode-1.42.0 → sourcecode-1.45.0}/.gitignore +0 -0
  12. {sourcecode-1.42.0 → sourcecode-1.45.0}/.ruff.toml +0 -0
  13. {sourcecode-1.42.0 → sourcecode-1.45.0}/CONTRIBUTING.md +0 -0
  14. {sourcecode-1.42.0 → sourcecode-1.45.0}/LICENSE +0 -0
  15. {sourcecode-1.42.0 → sourcecode-1.45.0}/SECURITY.md +0 -0
  16. {sourcecode-1.42.0 → sourcecode-1.45.0}/raw +0 -0
  17. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/adaptive_scanner.py +0 -0
  18. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/architecture_analyzer.py +0 -0
  19. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/architecture_summary.py +0 -0
  20. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/ast_extractor.py +0 -0
  21. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/cache.py +0 -0
  22. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/canonical_ir.py +0 -0
  23. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/cir_graphs.py +0 -0
  24. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/classifier.py +0 -0
  25. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/code_notes_analyzer.py +0 -0
  26. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/confidence_analyzer.py +0 -0
  27. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/context_scorer.py +0 -0
  28. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/context_summarizer.py +0 -0
  29. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/contract_model.py +0 -0
  30. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/contract_pipeline.py +0 -0
  31. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/coverage_parser.py +0 -0
  32. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/dependency_analyzer.py +0 -0
  33. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/detectors/__init__.py +0 -0
  34. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/detectors/base.py +0 -0
  35. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
  36. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/detectors/dart.py +0 -0
  37. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/detectors/dotnet.py +0 -0
  38. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/detectors/elixir.py +0 -0
  39. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/detectors/go.py +0 -0
  40. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/detectors/heuristic.py +0 -0
  41. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/detectors/hybrid.py +0 -0
  42. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/detectors/java.py +0 -0
  43. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
  44. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/detectors/nodejs.py +0 -0
  45. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/detectors/parsers.py +0 -0
  46. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/detectors/php.py +0 -0
  47. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/detectors/project.py +0 -0
  48. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/detectors/python.py +0 -0
  49. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/detectors/ruby.py +0 -0
  50. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/detectors/rust.py +0 -0
  51. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/detectors/systems.py +0 -0
  52. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/detectors/terraform.py +0 -0
  53. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/detectors/tooling.py +0 -0
  54. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/doc_analyzer.py +0 -0
  55. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/entrypoint_classifier.py +0 -0
  56. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/env_analyzer.py +0 -0
  57. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/error_schema.py +0 -0
  58. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/explain.py +0 -0
  59. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/file_chunker.py +0 -0
  60. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/file_classifier.py +0 -0
  61. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/flow_analyzer.py +0 -0
  62. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/format_contract.py +0 -0
  63. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/fqn_utils.py +0 -0
  64. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/git_analyzer.py +0 -0
  65. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/graph_analyzer.py +0 -0
  66. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/license.py +0 -0
  67. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/mcp/__init__.py +0 -0
  68. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
  69. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/mcp/onboarding/applier.py +0 -0
  70. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/mcp/onboarding/backup.py +0 -0
  71. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/mcp/onboarding/detector.py +0 -0
  72. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/mcp/onboarding/planner.py +0 -0
  73. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/mcp/registry.py +0 -0
  74. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/mcp/runner.py +0 -0
  75. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/mcp/server.py +0 -0
  76. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/mcp_nudge.py +0 -0
  77. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/metrics_analyzer.py +0 -0
  78. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/migrate_check.py +0 -0
  79. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/openapi_surface.py +0 -0
  80. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/output_budget.py +0 -0
  81. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/path_filters.py +0 -0
  82. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/pr_comment_renderer.py +0 -0
  83. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/pr_impact.py +0 -0
  84. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/prepare_context.py +0 -0
  85. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/progress.py +0 -0
  86. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/ranking_engine.py +0 -0
  87. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/redactor.py +0 -0
  88. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/relevance_scorer.py +0 -0
  89. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/rename_refactor.py +0 -0
  90. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/repo_classifier.py +0 -0
  91. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/ris.py +0 -0
  92. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/runtime_classifier.py +0 -0
  93. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/scanner.py +0 -0
  94. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/schema.py +0 -0
  95. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/security_config.py +0 -0
  96. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/semantic_analyzer.py +0 -0
  97. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/serializer.py +0 -0
  98. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/spring_event_topology.py +0 -0
  99. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/spring_findings.py +0 -0
  100. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/spring_impact.py +0 -0
  101. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/spring_model.py +0 -0
  102. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/spring_security_audit.py +0 -0
  103. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/spring_semantic.py +0 -0
  104. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/spring_tx_analyzer.py +0 -0
  105. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/summarizer.py +0 -0
  106. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/telemetry/__init__.py +0 -0
  107. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/telemetry/config.py +0 -0
  108. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/telemetry/consent.py +0 -0
  109. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/telemetry/events.py +0 -0
  110. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/telemetry/filters.py +0 -0
  111. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/telemetry/transport.py +0 -0
  112. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/tree_utils.py +0 -0
  113. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/version_check.py +0 -0
  114. {sourcecode-1.42.0 → sourcecode-1.45.0}/src/sourcecode/workspace.py +0 -0
  115. {sourcecode-1.42.0 → sourcecode-1.45.0}/supabase/functions/README.md +0 -0
  116. {sourcecode-1.42.0 → sourcecode-1.45.0}/supabase/functions/get-license/index.ts +0 -0
  117. {sourcecode-1.42.0 → sourcecode-1.45.0}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
  118. {sourcecode-1.42.0 → sourcecode-1.45.0}/supabase/functions/telemetry/index.ts +0 -0
  119. {sourcecode-1.42.0 → sourcecode-1.45.0}/supabase/sql/license_event_ordering.sql +0 -0
  120. {sourcecode-1.42.0 → sourcecode-1.45.0}/supabase/sql/telemetry_events.sql +0 -0
@@ -1,5 +1,74 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.45.0] — 2026-06-16
4
+
5
+ ### Fixed
6
+ - **P1 — `cache clear` hung indefinitely in non-interactive contexts (CI, MCP, pipes).**
7
+ Without `--yes`, the command called `click.confirm()`, which blocks reading stdin
8
+ forever when stdin is an open pipe that never sends EOF (exactly the CI/agent case) —
9
+ breaking pipelines and leaving the cache uncleared. The confirm prompt is now gated
10
+ behind `sys.stdin.isatty()`: interactive terminals still prompt, while non-interactive
11
+ runs proceed immediately (clear is idempotent; RIS stays preserved unless `--all`) and
12
+ print a one-line notice. Verified: old path times out on a never-EOF pipe, new path
13
+ returns in ~0.2s with exit 0.
14
+ - **`--no-cache` rejected by analysis subcommands ("No such option").** The flag existed
15
+ only on the root analysis command, so `sourcecode endpoints --no-cache` (and `validation`,
16
+ `spring-audit`, `migrate-check`, `impact`, `impact-chain`) aborted with exit 2, breaking
17
+ scripted invocations that pass a uniform flag. These subcommands always read fresh source,
18
+ so `--no-cache` is now accepted as a documented no-op on each.
19
+ - **`endpoints --limit` reported incoherent security counters.** `total` was recomputed to
20
+ the limited set while `no_security_signal` / `undocumented` kept their repo-wide values
21
+ (e.g. `total:2` next to `no_security_signal:3996`). The counters are now recomputed over
22
+ the filtered set; the repo-wide originals are preserved under `_filter.*_before_filter`.
23
+ - **`validation` returned all-zeros silently when no OpenAPI spec was present.** A repo with
24
+ no spec (no `target/generated-sources`/spec on disk) produced an empty result with no
25
+ explanation, easily misread as "no validation anywhere". The result now carries an explicit
26
+ `openapi_spec: null` + `note` field (also echoed to stderr) clarifying that declarative DTO
27
+ constraints can't be recovered without a spec and that zero counts are expected, not a finding.
28
+
29
+ ### Notes
30
+ - MCP orchestrator (`mcp/orchestrator.py`) gained a scoped TODO documenting three planned
31
+ high-value Java/Spring flow presets (`run_migrate_flow`, `run_security_audit_flow`, and an
32
+ R2-rule extension) — documented only, not implemented.
33
+
34
+ ## [1.44.0] — 2026-06-16
35
+
36
+ ### Fixed
37
+ - **CH-004c — annotation-free structural classes dropped their inheritance edges.**
38
+ `build_repo_ir`'s fast pre-scan skips files with no recognized annotation marker,
39
+ emitting only minimal class-name symbols (for same-package resolution) and **no
40
+ relations**. A class with no annotation and no injected field of its own that
41
+ participates purely structurally — `class X extends Base implements I {}` — therefore
42
+ lost its `extends`/`implements` edges, so `implementation_graph` could not link
43
+ sub→supertype and impact analysis could not traverse the hierarchy. The pre-scan now
44
+ also keeps a file when a type declaration carries an `extends`/`implements` clause
45
+ (`_INHERIT_PRESCAN_RE`), routing it through full extraction so its inheritance edges
46
+ are built and resolved in pass 2 with the same-package map. Annotation-free leaf
47
+ classes with no inheritance still skip, preserving the pre-scan optimization. Closes
48
+ the last open layer of CH-004 (a/b shipped in 1.43.0).
49
+
50
+ ## [1.43.0] — 2026-06-16
51
+
52
+ ### Fixed
53
+ - **CH-004a — impact graph dropped field-injection-only classes.** `build_repo_ir`'s fast
54
+ pre-scan skips files with no recognized annotation marker; the marker set included
55
+ `@Inject` but not `@Autowired`, `@Resource`, `@Qualifier`, or `@Value`. A class wired
56
+ purely by field injection with no class-level stereotype (e.g. an abstract base controller
57
+ that holds the services its concrete subclasses inherit) was skipped entirely, so its
58
+ `injects` edges never existed and `impact-chain` could not traverse through it. Added the
59
+ field/setter-injection annotations (`@Autowired`, `@Resource`, `@Qualifier`, `@Value`,
60
+ `@PersistenceContext`, `@PersistenceUnit`) to the pre-scan marker set.
61
+ - **CH-004b — same-package supertypes were not FQN-resolved.** The `extends`/`implements`
62
+ edge builder resolved supertype names only via `import_map`, so a same-package
63
+ `extends Base` (which needs no Java import) produced a **bare-name** edge target. The
64
+ `implementation_graph` could then not link sub→supertype, making same-package class
65
+ hierarchies invisible to impact analysis. Supertypes are now resolved via
66
+ `_resolve_dep_type` (import + same-package + wildcard), matching `injects`/constructor edges.
67
+
68
+ Both fixes were surfaced by a field test on BroadleafCommerce (2985-file monolith); they
69
+ under-reported blast radius on any large repo, not just that one. See
70
+ `docs/eval/2026-06-16-broadleaf-checkout-impact-fieldtest.md`.
71
+
3
72
  ## [1.42.0] — 2026-06-16
4
73
 
5
74
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 1.42.0
3
+ Version: 1.45.0
4
4
  Summary: Persistent structural context and ultra-fast repeated analysis for AI coding agents
5
5
  License-File: LICENSE
6
6
  Keywords: agents,ai,codebase,context,developer-tools,llm
@@ -40,7 +40,7 @@ Description-Content-Type: text/markdown
40
40
 
41
41
  **Persistent structural context and ultra-fast repeated analysis for AI coding agents.**
42
42
 
43
- ![Version](https://img.shields.io/badge/version-1.39.0-blue)
43
+ ![Version](https://img.shields.io/badge/version-1.45.0-blue)
44
44
  ![Python](https://img.shields.io/badge/python-3.9%2B-green)
45
45
 
46
46
  ---
@@ -114,7 +114,7 @@ pipx install sourcecode
114
114
 
115
115
  ```bash
116
116
  sourcecode version
117
- # sourcecode 1.39.0
117
+ # sourcecode 1.44.0
118
118
  ```
119
119
 
120
120
  ---
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Persistent structural context and ultra-fast repeated analysis for AI coding agents.**
4
4
 
5
- ![Version](https://img.shields.io/badge/version-1.39.0-blue)
5
+ ![Version](https://img.shields.io/badge/version-1.45.0-blue)
6
6
  ![Python](https://img.shields.io/badge/python-3.9%2B-green)
7
7
 
8
8
  ---
@@ -76,7 +76,7 @@ pipx install sourcecode
76
76
 
77
77
  ```bash
78
78
  sourcecode version
79
- # sourcecode 1.39.0
79
+ # sourcecode 1.44.0
80
80
  ```
81
81
 
82
82
  ---
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sourcecode"
7
- version = "1.42.0"
7
+ version = "1.45.0"
8
8
  description = "Persistent structural context and ultra-fast repeated analysis 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__ = "1.42.0"
3
+ __version__ = "1.45.0"
@@ -3685,6 +3685,10 @@ def impact_cmd(
3685
3685
  "-c",
3686
3686
  help="Copy output to system clipboard after a successful run. No-op when --output is used or clipboard is unavailable.",
3687
3687
  ),
3688
+ no_cache: bool = typer.Option(
3689
+ False, "--no-cache",
3690
+ help="Accepted for compatibility; this command always reads fresh source (no snapshot cache). No-op.",
3691
+ ),
3688
3692
  ) -> None:
3689
3693
  """Blast-radius analysis: who calls this class and what breaks if it changes?
3690
3694
 
@@ -3836,6 +3840,10 @@ def endpoints_cmd(
3836
3840
  None, "--limit", "-n",
3837
3841
  help="Maximum number of endpoints to return.",
3838
3842
  ),
3843
+ no_cache: bool = typer.Option(
3844
+ False, "--no-cache",
3845
+ help="Accepted for compatibility; this command always reads fresh source (no snapshot cache). No-op.",
3846
+ ),
3839
3847
  ) -> None:
3840
3848
  """Extract REST API endpoint surface from Java source files.
3841
3849
 
@@ -3899,13 +3907,26 @@ def endpoints_cmd(
3899
3907
  if limit is not None and limit > 0:
3900
3908
  endpoints_list = endpoints_list[:limit]
3901
3909
  if path_prefix or controller or limit is not None:
3910
+ # Preserve the repo-wide aggregates before overwriting them with the
3911
+ # filtered counts, so a --limit/--filter request stays internally
3912
+ # coherent (total must match the security counters it is reported with).
3913
+ _no_sec_before = data.get("no_security_signal")
3914
+ _undoc_before = data.get("undocumented")
3915
+ _no_sec_after = sum(
3916
+ 1 for e in endpoints_list
3917
+ if e.get("security", {}).get("policy") == "none_detected"
3918
+ )
3902
3919
  data["endpoints"] = endpoints_list
3903
3920
  data["total"] = len(endpoints_list)
3921
+ data["no_security_signal"] = _no_sec_after
3922
+ data["undocumented"] = _no_sec_after
3904
3923
  data["_filter"] = {
3905
3924
  "path_prefix": path_prefix,
3906
3925
  "controller": controller,
3907
3926
  "limit": limit,
3908
3927
  "total_before_filter": _total_before,
3928
+ "no_security_signal_before_filter": _no_sec_before,
3929
+ "undocumented_before_filter": _undoc_before,
3909
3930
  }
3910
3931
 
3911
3932
  output = _serialize_dict(data, format)
@@ -3948,6 +3969,10 @@ def validation_cmd(
3948
3969
  False, "--gaps-only",
3949
3970
  help="Report only endpoints/fields with no declared validation (the gaps section).",
3950
3971
  ),
3972
+ no_cache: bool = typer.Option(
3973
+ False, "--no-cache",
3974
+ help="Accepted for compatibility; this command always reads fresh source (no snapshot cache). No-op.",
3975
+ ),
3951
3976
  ) -> None:
3952
3977
  """Map request-body validation per endpoint (constraints + custom validators).
3953
3978
 
@@ -3997,14 +4022,21 @@ def validation_cmd(
3997
4022
  g for g in data.get("gaps", [])
3998
4023
  if str(g.get("path", "")).startswith(path_prefix)
3999
4024
  ]
4025
+ _note = data.get("note")
4000
4026
  if gaps_only:
4001
4027
  data = {
4002
4028
  "gaps": data.get("gaps", []),
4003
4029
  "summary": data.get("summary", {}),
4004
4030
  }
4031
+ if _note:
4032
+ data["note"] = _note
4005
4033
 
4006
4034
  output = _serialize_dict(data, format)
4007
4035
  _summary = data.get("summary", {})
4036
+ # Human-facing heads-up when the result is empty purely because no OpenAPI
4037
+ # spec was found — otherwise the all-zero JSON reads as a false negative.
4038
+ if _note:
4039
+ typer.echo(f"Note: {_note}", err=True)
4008
4040
  _emit_command_output(
4009
4041
  output, output_path, copy,
4010
4042
  success_msg=f"Validation surface written to {output_path} "
@@ -4133,6 +4165,10 @@ def spring_audit_cmd(
4133
4165
  "--ci/--no-ci",
4134
4166
  help="Exit with code 1 if any findings at or above --min-severity are found. For CI/CD gates.",
4135
4167
  ),
4168
+ no_cache: bool = typer.Option(
4169
+ False, "--no-cache",
4170
+ help="Accepted for compatibility; this command always reads fresh source (no snapshot cache). No-op.",
4171
+ ),
4136
4172
  ) -> None:
4137
4173
  """Spring semantic audit: TX anomalies (TX-001..005) + security surface (SEC-001..003).
4138
4174
 
@@ -4315,6 +4351,10 @@ def migrate_check_cmd(
4315
4351
  help="Minimum severity to include: critical, high, medium, or low (default).",
4316
4352
  show_default=True,
4317
4353
  ),
4354
+ no_cache: bool = typer.Option(
4355
+ False, "--no-cache",
4356
+ help="Accepted for compatibility; this command always reads fresh source (no snapshot cache). No-op.",
4357
+ ),
4318
4358
  ) -> None:
4319
4359
  """Spring Boot 2→3 migration readiness: detect javax→jakarta namespace blockers.
4320
4360
 
@@ -4423,6 +4463,10 @@ def impact_chain_cmd(
4423
4463
  help="Query type: impact (default) or events.",
4424
4464
  show_default=True,
4425
4465
  ),
4466
+ no_cache: bool = typer.Option(
4467
+ False, "--no-cache",
4468
+ help="Accepted for compatibility; this command always reads fresh source (no snapshot cache). No-op.",
4469
+ ),
4426
4470
  ) -> None:
4427
4471
  """Spring impact-chain: systemic blast radius of a symbol with TX/SEC enrichment.
4428
4472
 
@@ -6135,8 +6179,19 @@ def cache_clear_cmd(
6135
6179
  _clear_ris = include_ris or all_
6136
6180
  if not yes:
6137
6181
  _ris_note = " (including RIS)" if _clear_ris else " (RIS preserved — use --all to also clear it)"
6138
- import click as _click
6139
- _click.confirm(f"Delete all cache files for {target}{_ris_note}?", abort=True, err=True)
6182
+ # P1: never block on stdin in a non-interactive context (CI, MCP, pipes).
6183
+ # The interactive prompt is only meaningful on a TTY; elsewhere it would
6184
+ # hang indefinitely waiting for input. Treat non-interactive as confirmed
6185
+ # (clear is idempotent cleanup; RIS is preserved unless --all is passed).
6186
+ if sys.stdin.isatty():
6187
+ import click as _click
6188
+ _click.confirm(f"Delete all cache files for {target}{_ris_note}?", abort=True, err=True)
6189
+ else:
6190
+ typer.echo(
6191
+ f"Non-interactive: clearing cache for {target}{_ris_note}. "
6192
+ "Pass --yes to silence this notice.",
6193
+ err=True,
6194
+ )
6140
6195
  removed = _cm.clear(target, clear_ris=_clear_ris)
6141
6196
  typer.echo(f"Removed {removed} file(s).", err=True)
6142
6197
 
@@ -6,6 +6,29 @@ Converts the MCP from a flat tool collection into a guided agent operating syste
6
6
  - run_pr_review_flow: auto-chains delta + review_pr + blast radius
7
7
  - run_bug_investigation_flow: auto-chains fix_bug + impact + IR context
8
8
  - run_feature_flow: auto-chains context + endpoints + delta + structural awareness
9
+
10
+ TODO — planned high-value Java/Spring flow presets (audit 2026-06-16, repo SAINT):
11
+ These extend the existing orchestrator; they are NOT yet implemented.
12
+
13
+ 1. TODO: implement later — run_migrate_flow
14
+ Preset wrapping `migrate-check`. Primary high-value entry point for
15
+ Spring Boot 2→3 planning (audit: produces 1,356-file prioritized
16
+ inventory in ~6s — the strongest determinante win). Should surface
17
+ readiness_score, blocking count, per-target breakdown (jakarta /
18
+ spring_security_6 / java_11) and estimated_effort_days.
19
+
20
+ 2. TODO: implement later — run_security_audit_flow
21
+ Preset wrapping `spring-audit` + `endpoints`. Auto-handle the
22
+ config-less case: when no sourcecode.config.json is present and the
23
+ repo carries custom security annotations, emit a fallback WARNING +
24
+ hint to add sourcecode.config.json (customAnnotations) rather than
25
+ returning a misleading 100% none_detected result.
26
+
27
+ 3. TODO: implement later — extend R2 orchestration rule (apply_orchestration_rules)
28
+ Inject preset (1)/(2) when detected intent maps to migration or
29
+ security audit, mirroring the existing R2 java_no_endpoints rule.
30
+ Requires new INTENT_MIGRATION / INTENT_SECURITY_AUDIT constants +
31
+ _INTENT_PATTERNS entries + WORKFLOW_SEQUENCES / FLOW_RUNNERS wiring.
9
32
  """
10
33
  from __future__ import annotations
11
34
 
@@ -131,6 +131,15 @@ _CLASS_DECL_RE = re.compile(
131
131
  r'\s*\{',
132
132
  )
133
133
 
134
+ # CH-004c: detect a type declaration that participates structurally via inheritance
135
+ # (`class X extends Base` / `interface I extends A` / `class C implements I`). Used by
136
+ # the fast pre-scan to NOT skip annotation-free files that still own extends/implements
137
+ # edges the impact graph needs. `[^{;]*?` keeps the match within a single declaration
138
+ # head (stops at the body `{` or a `;`), matching the precision of _CLASS_DECL_RE.
139
+ _INHERIT_PRESCAN_RE = re.compile(
140
+ r'\b(?:class|interface)\s+[A-Z]\w*[^{;]*?\b(?:extends|implements)\b'
141
+ )
142
+
134
143
  _METHOD_DECL_RE = re.compile(
135
144
  r'^(?P<modifiers>(?:(?:public|private|protected|static|final|synchronized'
136
145
  r'|abstract|default|native|strictfp|override)\s+)*)'
@@ -1265,7 +1274,11 @@ def _build_relations(
1265
1274
  # commas so each base produces its own edge and the reverse graph sees
1266
1275
  # every supertype (not a single mangled token).
1267
1276
  for base in _split_supertype_list(extends_str):
1268
- to = import_map.get(base, base)
1277
+ # CH-004: resolve same-package / wildcard-imported supertypes to their
1278
+ # FQN (not just import_map), else a same-package `extends Base` stays a
1279
+ # bare name and the implementation_graph cannot link sub→supertype.
1280
+ _sbase = re.sub(r'<.*', '', base).strip()
1281
+ to = _resolve_dep_type(_sbase) or import_map.get(_sbase, base)
1269
1282
  edges.append(RelationEdge(
1270
1283
  from_symbol=class_fqn,
1271
1284
  to_symbol=to,
@@ -1276,7 +1289,8 @@ def _build_relations(
1276
1289
 
1277
1290
  if implements_str:
1278
1291
  for base in _split_supertype_list(implements_str):
1279
- to = import_map.get(base, base)
1292
+ _sbase = re.sub(r'<.*', '', base).strip()
1293
+ to = _resolve_dep_type(_sbase) or import_map.get(_sbase, base)
1280
1294
  edges.append(RelationEdge(
1281
1295
  from_symbol=class_fqn,
1282
1296
  to_symbol=to,
@@ -3127,6 +3141,14 @@ def build_repo_ir(
3127
3141
  '@RequiredArgsConstructor', '@AllArgsConstructor',
3128
3142
  '@Inject', '@ApplicationScoped', '@RequestScoped', '@Singleton',
3129
3143
  '@EnableMethodSecurity', '@EnableGlobalMethodSecurity',
3144
+ # Field/setter injection markers (CH-004). A class wired purely by field
3145
+ # injection — no class-level stereotype — is still a node in the DI graph:
3146
+ # e.g. an abstract base controller that holds @Autowired/@Resource services
3147
+ # its concrete subclasses inherit. Omitting these pre-scan-skips such classes,
3148
+ # dropping their injects edges, so impact-chain cannot reach callers through
3149
+ # them (Broadleaf: AbstractCheckoutController → checkout endpoints went missing).
3150
+ '@Autowired', '@Resource', '@Qualifier', '@Value',
3151
+ '@PersistenceContext', '@PersistenceUnit',
3130
3152
  # JPA / persistence (needed for stereotype detection in all commands)
3131
3153
  '@Entity', '@MappedSuperclass', '@Embeddable',
3132
3154
  # AOP / messaging / event sourcing
@@ -3173,7 +3195,8 @@ def build_repo_ir(
3173
3195
  _meta_chars_read += len(source)
3174
3196
  # Fast pre-scan: if file has no relevant annotations skip full extraction.
3175
3197
  # Still register package/class name for same-package resolution.
3176
- if not any(marker in source for marker in _effective_markers):
3198
+ if not any(marker in source for marker in _effective_markers) \
3199
+ and not _INHERIT_PRESCAN_RE.search(source):
3177
3200
  pkg_m = _PKG_RE.search(source)
3178
3201
  _pkg = pkg_m.group(1) if pkg_m else ""
3179
3202
  # Minimal class-name symbols for same-package map (no methods/fields)
@@ -302,4 +302,19 @@ def build_validation_surface(
302
302
  spec_path = endpoints_data.get("openapi_spec")
303
303
  if spec_path:
304
304
  result["openapi_spec"] = spec_path
305
+ else:
306
+ # No OpenAPI spec on disk / under target/generated-sources. Declarative
307
+ # DTO constraints cannot be recovered, so a sea of zeros here is expected
308
+ # and NOT a sign the repo lacks validation — it just isn't OpenAPI-driven.
309
+ # Surface this explicitly so the result is not silently misread as
310
+ # "no validation anywhere".
311
+ result["openapi_spec"] = None
312
+ result["note"] = (
313
+ "No OpenAPI spec found (no spec on disk or under "
314
+ "target/generated-sources). Declarative DTO constraints cannot be "
315
+ "recovered; only source-declared custom validators are reported. "
316
+ "Body-endpoint and validated-field counts will read zero unless an "
317
+ "OpenAPI spec is present — this is expected, not a missing-validation "
318
+ "finding."
319
+ )
305
320
  return result
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes