sourcecode 1.35.14__tar.gz → 1.35.15__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 (106) hide show
  1. {sourcecode-1.35.14 → sourcecode-1.35.15}/PKG-INFO +3 -3
  2. {sourcecode-1.35.14 → sourcecode-1.35.15}/README.md +2 -2
  3. {sourcecode-1.35.14 → sourcecode-1.35.15}/pyproject.toml +1 -1
  4. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/__init__.py +1 -1
  5. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/cir_graphs.py +9 -5
  6. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/explain.py +4 -2
  7. sourcecode-1.35.15/src/sourcecode/fqn_utils.py +53 -0
  8. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/pr_impact.py +3 -1
  9. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/spring_impact.py +7 -12
  10. {sourcecode-1.35.14 → sourcecode-1.35.15}/.github/workflows/build-windows.yml +0 -0
  11. {sourcecode-1.35.14 → sourcecode-1.35.15}/.gitignore +0 -0
  12. {sourcecode-1.35.14 → sourcecode-1.35.15}/.ruff.toml +0 -0
  13. {sourcecode-1.35.14 → sourcecode-1.35.15}/CHANGELOG.md +0 -0
  14. {sourcecode-1.35.14 → sourcecode-1.35.15}/CONTRIBUTING.md +0 -0
  15. {sourcecode-1.35.14 → sourcecode-1.35.15}/LICENSE +0 -0
  16. {sourcecode-1.35.14 → sourcecode-1.35.15}/SECURITY.md +0 -0
  17. {sourcecode-1.35.14 → sourcecode-1.35.15}/raw +0 -0
  18. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/adaptive_scanner.py +0 -0
  19. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/architecture_analyzer.py +0 -0
  20. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/architecture_summary.py +0 -0
  21. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/ast_extractor.py +0 -0
  22. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/cache.py +0 -0
  23. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/canonical_ir.py +0 -0
  24. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/classifier.py +0 -0
  25. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/cli.py +0 -0
  26. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/code_notes_analyzer.py +0 -0
  27. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/confidence_analyzer.py +0 -0
  28. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/context_scorer.py +0 -0
  29. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/context_summarizer.py +0 -0
  30. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/contract_model.py +0 -0
  31. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/contract_pipeline.py +0 -0
  32. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/coverage_parser.py +0 -0
  33. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/dependency_analyzer.py +0 -0
  34. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/detectors/__init__.py +0 -0
  35. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/detectors/base.py +0 -0
  36. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/detectors/csproj_parser.py +0 -0
  37. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/detectors/dart.py +0 -0
  38. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/detectors/dotnet.py +0 -0
  39. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/detectors/elixir.py +0 -0
  40. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/detectors/go.py +0 -0
  41. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/detectors/heuristic.py +0 -0
  42. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/detectors/hybrid.py +0 -0
  43. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/detectors/java.py +0 -0
  44. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/detectors/jvm_ext.py +0 -0
  45. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/detectors/nodejs.py +0 -0
  46. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/detectors/parsers.py +0 -0
  47. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/detectors/php.py +0 -0
  48. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/detectors/project.py +0 -0
  49. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/detectors/python.py +0 -0
  50. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/detectors/ruby.py +0 -0
  51. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/detectors/rust.py +0 -0
  52. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/detectors/systems.py +0 -0
  53. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/detectors/terraform.py +0 -0
  54. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/detectors/tooling.py +0 -0
  55. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/doc_analyzer.py +0 -0
  56. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/entrypoint_classifier.py +0 -0
  57. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/env_analyzer.py +0 -0
  58. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/error_schema.py +0 -0
  59. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/file_classifier.py +0 -0
  60. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/flow_analyzer.py +0 -0
  61. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/git_analyzer.py +0 -0
  62. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/graph_analyzer.py +0 -0
  63. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/license.py +0 -0
  64. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/mcp/__init__.py +0 -0
  65. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
  66. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/mcp/onboarding/applier.py +0 -0
  67. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/mcp/onboarding/backup.py +0 -0
  68. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/mcp/onboarding/detector.py +0 -0
  69. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/mcp/onboarding/planner.py +0 -0
  70. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/mcp/orchestrator.py +0 -0
  71. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/mcp/registry.py +0 -0
  72. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/mcp/runner.py +0 -0
  73. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/mcp/server.py +0 -0
  74. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/mcp_nudge.py +0 -0
  75. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/metrics_analyzer.py +0 -0
  76. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/output_budget.py +0 -0
  77. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/path_filters.py +0 -0
  78. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/pr_comment_renderer.py +0 -0
  79. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/prepare_context.py +0 -0
  80. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/progress.py +0 -0
  81. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/ranking_engine.py +0 -0
  82. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/redactor.py +0 -0
  83. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/relevance_scorer.py +0 -0
  84. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/repo_classifier.py +0 -0
  85. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/repository_ir.py +0 -0
  86. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/ris.py +0 -0
  87. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/runtime_classifier.py +0 -0
  88. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/scanner.py +0 -0
  89. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/schema.py +0 -0
  90. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/semantic_analyzer.py +0 -0
  91. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/serializer.py +0 -0
  92. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/spring_event_topology.py +0 -0
  93. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/spring_findings.py +0 -0
  94. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/spring_model.py +0 -0
  95. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/spring_security_audit.py +0 -0
  96. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/spring_semantic.py +0 -0
  97. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/spring_tx_analyzer.py +0 -0
  98. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/summarizer.py +0 -0
  99. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/telemetry/__init__.py +0 -0
  100. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/telemetry/config.py +0 -0
  101. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/telemetry/consent.py +0 -0
  102. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/telemetry/events.py +0 -0
  103. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/telemetry/filters.py +0 -0
  104. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/telemetry/transport.py +0 -0
  105. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/tree_utils.py +0 -0
  106. {sourcecode-1.35.14 → sourcecode-1.35.15}/src/sourcecode/workspace.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 1.35.14
3
+ Version: 1.35.15
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
@@ -39,7 +39,7 @@ Description-Content-Type: text/markdown
39
39
 
40
40
  **Persistent structural context and ultra-fast repeated analysis for AI coding agents.**
41
41
 
42
- ![Version](https://img.shields.io/badge/version-1.35.14-blue)
42
+ ![Version](https://img.shields.io/badge/version-1.35.15-blue)
43
43
  ![Python](https://img.shields.io/badge/python-3.10%2B-green)
44
44
 
45
45
  ---
@@ -113,7 +113,7 @@ pipx install sourcecode
113
113
 
114
114
  ```bash
115
115
  sourcecode version
116
- # sourcecode 1.35.14
116
+ # sourcecode 1.35.15
117
117
  ```
118
118
 
119
119
  ---
@@ -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.35.14-blue)
5
+ ![Version](https://img.shields.io/badge/version-1.35.15-blue)
6
6
  ![Python](https://img.shields.io/badge/python-3.10%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.35.14
79
+ # sourcecode 1.35.15
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.35.14"
7
+ version = "1.35.15"
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.35.14"
3
+ __version__ = "1.35.15"
@@ -13,6 +13,8 @@ from __future__ import annotations
13
13
 
14
14
  from dataclasses import dataclass, field
15
15
 
16
+ from sourcecode.fqn_utils import normalize_owner_fqn
17
+
16
18
  # ---------------------------------------------------------------------------
17
19
  # ImplementationGraph — CH-001
18
20
  # ---------------------------------------------------------------------------
@@ -179,12 +181,14 @@ class InjectionGraph:
179
181
  if not from_fqn or not to_fqn:
180
182
  continue
181
183
 
182
- # Resolve injector to class level
183
- if "#" in from_fqn:
184
- class_fqn = from_fqn.rsplit("#", 1)[0]
184
+ # Resolve injector to class level.
185
+ # Three formats emitted by the CIR parser:
186
+ # Constructor: pkg.Class#<init> → class = pkg.Class
187
+ # Field: pkg.Class.field → class = pkg.Class (normalize_owner_fqn)
188
+ # Lombok: pkg.Class → class = pkg.Class (already class-level)
189
+ class_fqn = normalize_owner_fqn(from_fqn)
190
+ if class_fqn != from_fqn:
185
191
  injector_to_class[from_fqn] = class_fqn
186
- else:
187
- class_fqn = from_fqn
188
192
 
189
193
  # Build class → [dep, ...] and service → [class, ...] indices
190
194
  deps = deps_of.setdefault(class_fqn, [])
@@ -13,6 +13,8 @@ from __future__ import annotations
13
13
  from dataclasses import dataclass, field
14
14
  from typing import TYPE_CHECKING, Optional
15
15
 
16
+ from sourcecode.fqn_utils import normalize_owner_fqn
17
+
16
18
  if TYPE_CHECKING:
17
19
  from sourcecode.canonical_ir import CanonicalRepositoryIR
18
20
  from sourcecode.spring_model import SpringSemanticModel
@@ -255,8 +257,8 @@ def _build_callers(class_fqn: str, cir: "CanonicalRepositoryIR") -> list[str]:
255
257
  rev: dict = (getattr(cir, "reverse_graph", None) or {}).get(class_fqn) or {}
256
258
  for callers in rev.values():
257
259
  for caller_fqn in (callers or []):
258
- # Strip method part to get class
259
- cls_fqn = caller_fqn.rsplit("#", 1)[0] if "#" in caller_fqn else caller_fqn
260
+ # Normalize: field (pkg.Class.field) and method (pkg.Class#method) class FQN
261
+ cls_fqn = normalize_owner_fqn(caller_fqn)
260
262
  if cls_fqn == class_fqn:
261
263
  continue
262
264
  s = _simple(cls_fqn)
@@ -0,0 +1,53 @@
1
+ """fqn_utils.py — FQN normalization utilities (single source of truth).
2
+
3
+ All code that needs to distinguish class FQNs from member FQNs (methods, fields,
4
+ constructors) must use these functions. No direct `.split("#")`, `.rsplit(".")`,
5
+ or lowercase-heuristic checks elsewhere.
6
+
7
+ Symbol FQN conventions in the CIR:
8
+ Class/Interface/Enum: pkg.ClassName (no # or lowercase-last-seg)
9
+ Method: pkg.ClassName#methodName (hash separator)
10
+ Constructor: pkg.ClassName#<init> (hash, angle-bracket name)
11
+ Field: pkg.ClassName.fieldName (dot separator, lowercase last segment)
12
+ Inner class: pkg.ClassName.InnerClass (dot separator, uppercase last segment)
13
+ """
14
+ from __future__ import annotations
15
+
16
+
17
+ def normalize_owner_fqn(fqn: str) -> str:
18
+ """Extract the owning class FQN from any symbol FQN.
19
+
20
+ Examples:
21
+ PatientServiceImpl -> PatientServiceImpl
22
+ org.foo.PatientServiceImpl -> org.foo.PatientServiceImpl
23
+ org.foo.PatientServiceImpl#savePatient -> org.foo.PatientServiceImpl
24
+ org.foo.PatientServiceImpl#<init> -> org.foo.PatientServiceImpl
25
+ org.foo.PatientServiceImpl.dao -> org.foo.PatientServiceImpl
26
+ org.foo.PatientServiceImpl.InnerClass -> org.foo.PatientServiceImpl.InnerClass (unchanged)
27
+ """
28
+ if "#" in fqn:
29
+ return fqn.rsplit("#", 1)[0]
30
+ if "." in fqn:
31
+ last_seg = fqn.rsplit(".", 1)[1]
32
+ if last_seg and last_seg[0].islower():
33
+ return fqn.rsplit(".", 1)[0]
34
+ return fqn
35
+
36
+
37
+ def is_member_fqn(fqn: str) -> bool:
38
+ """Return True for method/field/constructor FQNs; False for type FQNs.
39
+
40
+ True: pkg.Class#method, pkg.Class#<init>, pkg.Class.fieldName
41
+ False: pkg.Class, pkg.outer.InnerClass, simple.Name
42
+ """
43
+ if "#" in fqn:
44
+ return True
45
+ if "." in fqn:
46
+ last_seg = fqn.rsplit(".", 1)[1]
47
+ return bool(last_seg and last_seg[0].islower())
48
+ return False
49
+
50
+
51
+ def is_type_fqn(fqn: str) -> bool:
52
+ """Return True for class/interface/enum/record FQNs; False for member FQNs."""
53
+ return not is_member_fqn(fqn)
@@ -20,6 +20,8 @@ from dataclasses import dataclass, field
20
20
  from pathlib import Path
21
21
  from typing import TYPE_CHECKING, Optional
22
22
 
23
+ from sourcecode.fqn_utils import is_member_fqn
24
+
23
25
  if TYPE_CHECKING:
24
26
  from sourcecode.canonical_ir import CanonicalRepositoryIR
25
27
  from sourcecode.spring_model import SpringSemanticModel
@@ -147,7 +149,7 @@ def _build_file_class_index(cir: "CanonicalRepositoryIR") -> dict[str, list[str]
147
149
  for node in nodes:
148
150
  fqn: str = node.get("fqn") or ""
149
151
  sf: str = node.get("source_file") or ""
150
- if not fqn or not sf or "#" in fqn:
152
+ if not fqn or not sf or is_member_fqn(fqn):
151
153
  continue
152
154
  index.setdefault(sf, []).append(fqn)
153
155
  return index
@@ -25,6 +25,7 @@ from dataclasses import dataclass, field
25
25
  from pathlib import Path
26
26
  from typing import TYPE_CHECKING, Optional
27
27
 
28
+ from sourcecode.fqn_utils import normalize_owner_fqn
28
29
  from sourcecode.spring_findings import SEVERITY_ORDER, SpringFinding
29
30
  from sourcecode.spring_model import SpringSemanticModel
30
31
 
@@ -311,19 +312,13 @@ def _bfs_callers(
311
312
  if etype in _SKIP_EDGE_TYPES:
312
313
  continue
313
314
  for caller in fqn_list:
314
- _add_caller(caller, depth)
315
- # CH-002: injects edge to a field/constructor node → also traverse
316
- # the containing class, bypassing the skipped contained_in edge.
317
- # Two formats emitted by the CIR parser:
318
- # Constructor injection: pkg.Class#<init> (hash separator)
319
- # Field injection: pkg.Class.field (dot, lowercase last segment)
320
315
  if etype == "injects":
321
- if "#" in caller:
322
- _add_caller(caller.rsplit("#", 1)[0], depth)
323
- elif "." in caller:
324
- last_seg = caller.rsplit(".", 1)[1]
325
- if last_seg and last_seg[0].islower():
326
- _add_caller(caller.rsplit(".", 1)[0], depth)
316
+ # CH-002: field (pkg.Class.field) and constructor (pkg.Class#<init>)
317
+ # FQNs are injection sites, not callers. Normalize to owning class so
318
+ # member FQNs never appear in direct_callers / indirect_callers.
319
+ _add_caller(normalize_owner_fqn(caller), depth)
320
+ else:
321
+ _add_caller(caller, depth)
327
322
 
328
323
  return direct, indirect, was_truncated
329
324
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes