sourcecode 0.23.0__py3-none-any.whl → 0.24.0__py3-none-any.whl

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.
sourcecode/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """sourcecode — Genera mapas de contexto estructurado para agentes IA."""
2
2
 
3
- __version__ = "0.23.0"
3
+ __version__ = "0.24.0"
@@ -74,6 +74,8 @@ class ArchitectureSummarizer:
74
74
  lang_lines = self._summarize_go_entry(entry_point.path, content)
75
75
  elif suffix in _JAVA_EXTENSIONS:
76
76
  lang_lines = self._summarize_java_entry(entry_point.path, content, sm.stacks)
77
+ elif suffix in {".cs", ".fs", ".vb"}:
78
+ lang_lines = self._summarize_dotnet_entry(sm.stacks)
77
79
  else:
78
80
  lang_lines = []
79
81
 
@@ -189,6 +191,44 @@ class ArchitectureSummarizer:
189
191
  lines.append("Orquesta el arranque de la aplicacion JVM.")
190
192
  return lines
191
193
 
194
+ def _summarize_dotnet_entry(self, stacks: list[StackDetection]) -> list[str]:
195
+ dotnet_stacks = [s for s in stacks if s.stack == "dotnet"]
196
+ if not dotnet_stacks:
197
+ return []
198
+ lines: list[str] = []
199
+ signals = [sig for s in dotnet_stacks for sig in s.signals]
200
+
201
+ for sig in signals:
202
+ if "project" in sig and "detected" in sig:
203
+ lines.append(sig[0].upper() + sig[1:] + ".")
204
+ break
205
+
206
+ for sig in signals:
207
+ if sig.startswith("project types:"):
208
+ types = sig.removeprefix("project types:").strip()
209
+ lines.append(f"Stack: {types}.")
210
+ break
211
+
212
+ for sig in signals:
213
+ if sig.startswith("target frameworks:"):
214
+ fws = sig.removeprefix("target frameworks:").strip()
215
+ lines.append(f"Target: {fws}.")
216
+ break
217
+
218
+ for sig in signals:
219
+ if sig.startswith("architecture:"):
220
+ pattern = sig.removeprefix("architecture:").strip()
221
+ lines.append(f"Patrón detectado: {pattern}.")
222
+ break
223
+
224
+ framework_names = [f.name for s in dotnet_stacks for f in s.frameworks]
225
+ if framework_names:
226
+ lines.append(f"Frameworks: {', '.join(framework_names)}.")
227
+
228
+ if not lines:
229
+ lines.append("Solución .NET detectada.")
230
+ return lines
231
+
192
232
  def _summarize_go_entry(self, path: str, content: str) -> list[str]:
193
233
  imports = re.findall(r'"([^"]+)"', content)
194
234
  internal = [module for module in imports if not module.startswith(("fmt", "net/", "os", "context"))]
@@ -273,6 +313,16 @@ class ArchitectureSummarizer:
273
313
  confidence="medium",
274
314
  )
275
315
  )
316
+ elif path.endswith("Program.cs") or path.endswith("Program.fs"):
317
+ candidates.append(
318
+ EntryPoint(
319
+ path=path,
320
+ stack="dotnet",
321
+ kind="cli",
322
+ source="convention",
323
+ confidence="medium",
324
+ )
325
+ )
276
326
  return candidates
277
327
 
278
328
  def _fallback_priority(self, path: str) -> tuple[int, int, str]:
@@ -0,0 +1,203 @@
1
+ from __future__ import annotations
2
+
3
+ import xml.etree.ElementTree as ET
4
+ from dataclasses import dataclass, field
5
+ from pathlib import Path, PurePosixPath
6
+
7
+
8
+ @dataclass
9
+ class CsprojProject:
10
+ name: str
11
+ path: str # repo-relative path to .csproj
12
+ project_dir: str # repo-relative folder (empty if root)
13
+ target_frameworks: list[str] = field(default_factory=list)
14
+ output_type: str = ""
15
+ sdk: str = ""
16
+ project_references: list[str] = field(default_factory=list) # resolved repo-relative csproj paths
17
+ package_references: list[tuple[str, str]] = field(default_factory=list)
18
+ project_type: str = "classlib" # webapi|classlib|console|test|blazor|worker
19
+ language: str = "csharp" # csharp|fsharp|vbnet
20
+
21
+
22
+ _TEST_PACKAGES: frozenset[str] = frozenset({
23
+ "xunit", "nunit", "mstest.testframework", "microsoft.net.test.sdk",
24
+ "nunit3testadapter", "xunit.runner.visualstudio", "mstest.testadapter",
25
+ "coverlet.collector", "moq", "nsubstitute", "fluentassertions",
26
+ })
27
+
28
+ _LAYER_KEYWORDS: tuple[tuple[str, str], ...] = (
29
+ ("api", "Api"),
30
+ ("web", "Api"),
31
+ ("endpoint", "Api"),
32
+ ("controller", "Api"),
33
+ ("application", "Application"),
34
+ ("usecase", "Application"),
35
+ ("usecases", "Application"),
36
+ ("domain", "Domain"),
37
+ ("core", "Domain"),
38
+ ("infrastructure", "Infrastructure"),
39
+ ("infra", "Infrastructure"),
40
+ ("persistence", "Infrastructure"),
41
+ ("repository", "Infrastructure"),
42
+ ("shared", "Shared"),
43
+ ("common", "Shared"),
44
+ ("contracts", "Shared"),
45
+ ("abstractions", "Shared"),
46
+ ("test", "Tests"),
47
+ ("tests", "Tests"),
48
+ ("spec", "Tests"),
49
+ ("specs", "Tests"),
50
+ )
51
+
52
+
53
+ def parse_csproj(absolute_path: Path, relative_path: str) -> CsprojProject | None:
54
+ """Parse a .csproj/.fsproj/.vbproj and return structured project info. Returns None on error."""
55
+ try:
56
+ content = absolute_path.read_text(encoding="utf-8", errors="replace")
57
+ root_elem = ET.fromstring(content)
58
+ except (OSError, ET.ParseError):
59
+ return None
60
+
61
+ suffix = Path(relative_path).suffix.lower()
62
+ language = {"fsproj": "fsharp", "vbproj": "vbnet"}.get(suffix.lstrip("."), "csharp")
63
+
64
+ project_dir = str(PurePosixPath(relative_path).parent)
65
+ if project_dir == ".":
66
+ project_dir = ""
67
+
68
+ name = Path(relative_path).stem
69
+ sdk = root_elem.get("Sdk", "") or ""
70
+
71
+ frameworks: list[str] = []
72
+ output_type = ""
73
+ package_refs: list[tuple[str, str]] = []
74
+ project_refs: list[str] = []
75
+
76
+ for elem in root_elem.iter():
77
+ tag = _strip_ns(elem.tag)
78
+ text = (elem.text or "").strip()
79
+
80
+ if tag == "TargetFramework":
81
+ if not frameworks and text:
82
+ frameworks = [text]
83
+ elif tag == "TargetFrameworks":
84
+ if text:
85
+ frameworks = [f.strip() for f in text.split(";") if f.strip()]
86
+ elif tag == "OutputType":
87
+ if not output_type:
88
+ output_type = text
89
+ elif tag == "AssemblyName":
90
+ if text:
91
+ name = text
92
+ elif tag == "PackageReference":
93
+ pkg = elem.get("Include", "") or ""
94
+ ver = elem.get("Version", "") or (elem.findtext("Version") or "").strip()
95
+ if pkg:
96
+ package_refs.append((pkg, ver))
97
+ elif tag == "ProjectReference":
98
+ include = (elem.get("Include", "") or "").replace("\\", "/")
99
+ if include:
100
+ project_refs.append(include)
101
+
102
+ if not sdk:
103
+ sdk = _detect_sdk_from_imports(root_elem)
104
+
105
+ resolved_refs = [
106
+ r for r in (_resolve_ref(relative_path, ref) for ref in project_refs) if r
107
+ ]
108
+
109
+ project_type = _classify_project(
110
+ sdk=sdk,
111
+ output_type=output_type,
112
+ name=name,
113
+ package_refs=[p[0] for p in package_refs],
114
+ )
115
+
116
+ return CsprojProject(
117
+ name=name,
118
+ path=relative_path,
119
+ project_dir=project_dir,
120
+ target_frameworks=frameworks,
121
+ output_type=output_type,
122
+ sdk=sdk,
123
+ project_references=resolved_refs,
124
+ package_references=package_refs,
125
+ project_type=project_type,
126
+ language=language,
127
+ )
128
+
129
+
130
+ def infer_architecture_pattern(projects: list[CsprojProject]) -> str | None:
131
+ """Infer Clean Architecture / Onion / Layered from project names."""
132
+ layers: set[str] = set()
133
+ for project in projects:
134
+ name_lower = project.name.lower()
135
+ for keyword, layer in _LAYER_KEYWORDS:
136
+ if keyword in name_lower:
137
+ layers.add(layer)
138
+ break
139
+
140
+ if {"Application", "Domain", "Infrastructure"} <= layers:
141
+ return "Clean Architecture"
142
+ if {"Api", "Domain", "Infrastructure"} <= layers:
143
+ return "Onion Architecture"
144
+ if {"Api", "Application", "Infrastructure"} <= layers:
145
+ return "Layered Architecture"
146
+ if len(layers) >= 3:
147
+ return "Layered Architecture"
148
+ return None
149
+
150
+
151
+ def _strip_ns(tag: str) -> str:
152
+ return tag.split("}", 1)[1] if "}" in tag else tag
153
+
154
+
155
+ def _resolve_ref(source_csproj: str, ref: str) -> str | None:
156
+ """Resolve a ProjectReference path (relative to source .csproj) to a repo-relative path."""
157
+ source_dir = str(PurePosixPath(source_csproj).parent)
158
+ base = "" if source_dir == "." else source_dir
159
+ combined = f"{base}/{ref}" if base else ref
160
+ parts: list[str] = []
161
+ for part in combined.split("/"):
162
+ if part == "..":
163
+ if parts:
164
+ parts.pop()
165
+ elif part and part != ".":
166
+ parts.append(part)
167
+ return "/".join(parts) if parts else None
168
+
169
+
170
+ def _detect_sdk_from_imports(root_elem: ET.Element) -> str:
171
+ for elem in root_elem.iter():
172
+ if _strip_ns(elem.tag) == "Import":
173
+ project = elem.get("Project", "")
174
+ if "Microsoft.NET" in project:
175
+ return project
176
+ return ""
177
+
178
+
179
+ def _classify_project(
180
+ *,
181
+ sdk: str,
182
+ output_type: str,
183
+ name: str,
184
+ package_refs: list[str],
185
+ ) -> str:
186
+ sdk_l = sdk.lower()
187
+ name_l = name.lower()
188
+ pkgs_l = {p.lower() for p in package_refs}
189
+ out_l = output_type.lower()
190
+
191
+ if pkgs_l & _TEST_PACKAGES or any(kw in name_l for kw in ("test", "tests", "spec", "specs")):
192
+ return "test"
193
+ if "microsoft.net.sdk.blazor" in sdk_l or any("blazor" in p for p in pkgs_l):
194
+ return "blazor"
195
+ if "microsoft.net.sdk.web" in sdk_l or any(
196
+ p.startswith("microsoft.aspnetcore") for p in pkgs_l
197
+ ):
198
+ return "webapi"
199
+ if "microsoft.net.sdk.worker" in sdk_l or "microsoft.extensions.hosting" in pkgs_l:
200
+ return "worker"
201
+ if out_l in ("exe", "winexe"):
202
+ return "console"
203
+ return "classlib"
@@ -1,15 +1,39 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from collections import Counter
4
+
3
5
  from sourcecode.detectors.base import (
4
6
  AbstractDetector,
5
7
  DetectionContext,
6
8
  EntryPoint,
7
9
  StackDetection,
8
10
  )
9
- from sourcecode.detectors.parsers import read_text_lines, unique_strings
11
+ from sourcecode.detectors.csproj_parser import (
12
+ CsprojProject,
13
+ infer_architecture_pattern,
14
+ parse_csproj,
15
+ )
10
16
  from sourcecode.schema import FrameworkDetection
11
17
  from sourcecode.tree_utils import flatten_file_tree, path_exists_in_tree
12
18
 
19
+ _PROJECT_EXTENSIONS = (".csproj", ".fsproj", ".vbproj")
20
+
21
+ _FRAMEWORK_SIGNALS: tuple[tuple[str, str], ...] = (
22
+ ("microsoft.net.sdk.web", "ASP.NET Core"),
23
+ ("microsoft.net.sdk.blazor", "Blazor"),
24
+ ("microsoft.net.sdk.worker", "Worker Service"),
25
+ ("microsoft.aspnetcore", "ASP.NET Core"),
26
+ )
27
+
28
+ _ENTRY_CANDIDATES = (
29
+ "Program.cs",
30
+ "src/Program.cs",
31
+ "Program.fs",
32
+ "src/Program.fs",
33
+ "Startup.cs",
34
+ "src/Startup.cs",
35
+ )
36
+
13
37
 
14
38
  class DotnetDetector(AbstractDetector):
15
39
  name = "dotnet"
@@ -17,35 +41,25 @@ class DotnetDetector(AbstractDetector):
17
41
 
18
42
  def can_detect(self, context: DetectionContext) -> bool:
19
43
  return any(
20
- path.endswith((".csproj", ".sln"))
44
+ path.endswith((*_PROJECT_EXTENSIONS, ".sln"))
21
45
  for path in flatten_file_tree(context.file_tree)
22
46
  )
23
47
 
24
48
  def detect(self, context: DetectionContext) -> tuple[list[StackDetection], list[EntryPoint]]:
25
- manifests = [
26
- path
27
- for path in flatten_file_tree(context.file_tree)
28
- if path.endswith((".csproj", ".sln"))
29
- ]
30
- content = "\n".join(
31
- "\n".join(read_text_lines(context.root / manifest))
32
- for manifest in manifests
33
- ).lower()
49
+ all_paths = flatten_file_tree(context.file_tree)
50
+ project_paths = [p for p in all_paths if p.endswith(_PROJECT_EXTENSIONS)]
34
51
 
35
- frameworks: list[FrameworkDetection] = []
36
- if "microsoft.net.sdk.web" in content or "microsoft.aspnetcore" in content:
37
- frameworks.append(FrameworkDetection(name="ASP.NET Core", source="manifest"))
52
+ projects: list[CsprojProject] = []
53
+ for rel_path in project_paths:
54
+ project = parse_csproj(context.root / rel_path, rel_path)
55
+ if project is not None:
56
+ projects.append(project)
38
57
 
39
- entry_points: list[EntryPoint] = []
40
- for path in unique_strings(
41
- ["Program.cs", "src/Program.cs", "Program.fs", "src/Program.fs"]
42
- ):
43
- if path_exists_in_tree(context.file_tree, path):
44
- kind = "api" if frameworks else "cli"
45
- entry_points.append(
46
- EntryPoint(path=path, stack="dotnet", kind=kind, source="manifest")
47
- )
58
+ frameworks = self._collect_frameworks(projects)
59
+ signals = self._build_signals(projects)
60
+ entry_points = self._detect_entry_points(context, projects, frameworks)
48
61
 
62
+ manifests = project_paths + [p for p in all_paths if p.endswith(".sln")]
49
63
  stack = StackDetection(
50
64
  stack="dotnet",
51
65
  detection_method="manifest",
@@ -53,5 +67,116 @@ class DotnetDetector(AbstractDetector):
53
67
  frameworks=frameworks,
54
68
  package_manager="nuget",
55
69
  manifests=manifests,
70
+ signals=signals,
56
71
  )
57
72
  return [stack], entry_points
73
+
74
+ def _collect_frameworks(self, projects: list[CsprojProject]) -> list[FrameworkDetection]:
75
+ seen: dict[str, FrameworkDetection] = {}
76
+ for project in projects:
77
+ content_lower = project.sdk.lower()
78
+ for pkg_name, _ in project.package_references:
79
+ content_lower += " " + pkg_name.lower()
80
+ for signal, framework_name in _FRAMEWORK_SIGNALS:
81
+ if signal in content_lower and framework_name not in seen:
82
+ seen[framework_name] = FrameworkDetection(name=framework_name, source="manifest")
83
+ return list(seen.values())
84
+
85
+ def _build_signals(self, projects: list[CsprojProject]) -> list[str]:
86
+ if not projects:
87
+ return []
88
+ signals: list[str] = []
89
+
90
+ count = len(projects)
91
+ signals.append(f"{count} project{'s' if count != 1 else ''} detected")
92
+
93
+ type_counter: Counter[str] = Counter(p.project_type for p in projects)
94
+ type_parts = []
95
+ for ptype, n in type_counter.most_common():
96
+ type_parts.append(f"{ptype}×{n}" if n > 1 else ptype)
97
+ signals.append(f"project types: {', '.join(type_parts)}")
98
+
99
+ frameworks_seen: set[str] = set()
100
+ all_frameworks: list[str] = []
101
+ for project in projects:
102
+ for fw in project.target_frameworks:
103
+ if fw and fw not in frameworks_seen:
104
+ frameworks_seen.add(fw)
105
+ all_frameworks.append(fw)
106
+ if all_frameworks:
107
+ signals.append(f"target frameworks: {', '.join(all_frameworks[:4])}")
108
+
109
+ pattern = infer_architecture_pattern(projects)
110
+ if pattern:
111
+ signals.append(f"architecture: {pattern}")
112
+
113
+ return signals
114
+
115
+ def _detect_entry_points(
116
+ self,
117
+ context: DetectionContext,
118
+ projects: list[CsprojProject],
119
+ frameworks: list[FrameworkDetection],
120
+ ) -> list[EntryPoint]:
121
+ entry_points: list[EntryPoint] = []
122
+ framework_names = {f.name for f in frameworks}
123
+ has_web = bool(framework_names & {"ASP.NET Core", "Blazor"})
124
+
125
+ # Entry points from exe/web projects
126
+ for project in projects:
127
+ if project.project_type in ("webapi", "console", "worker", "blazor"):
128
+ kind = _project_type_to_entry_kind(project.project_type, has_web)
129
+ # Look for Program.cs/Startup.cs relative to the project dir
130
+ for candidate in _entry_file_candidates(project):
131
+ if path_exists_in_tree(context.file_tree, candidate):
132
+ entry_points.append(
133
+ EntryPoint(
134
+ path=candidate,
135
+ stack="dotnet",
136
+ kind=kind,
137
+ source="manifest",
138
+ confidence="high",
139
+ )
140
+ )
141
+ break
142
+
143
+ # Fallback: known entry file patterns
144
+ if not entry_points:
145
+ kind = "api" if has_web else "cli"
146
+ for candidate in _ENTRY_CANDIDATES:
147
+ if path_exists_in_tree(context.file_tree, candidate):
148
+ entry_points.append(
149
+ EntryPoint(
150
+ path=candidate,
151
+ stack="dotnet",
152
+ kind=kind,
153
+ source="manifest",
154
+ confidence="medium",
155
+ )
156
+ )
157
+ break
158
+
159
+ return entry_points
160
+
161
+
162
+ def _project_type_to_entry_kind(project_type: str, has_web: bool) -> str:
163
+ if project_type == "webapi":
164
+ return "api"
165
+ if project_type == "blazor":
166
+ return "web"
167
+ if project_type == "worker":
168
+ return "server"
169
+ return "cli"
170
+
171
+
172
+ def _entry_file_candidates(project: CsprojProject) -> list[str]:
173
+ prefix = f"{project.project_dir}/" if project.project_dir else ""
174
+ candidates = [
175
+ f"{prefix}Program.cs",
176
+ f"{prefix}Program.fs",
177
+ f"{prefix}Startup.cs",
178
+ ]
179
+ # Also check without prefix for root-level projects
180
+ if prefix:
181
+ candidates += ["Program.cs", "src/Program.cs"]
182
+ return candidates
@@ -21,6 +21,7 @@ class GraphAnalyzer:
21
21
  _NODE_EXTENSIONS = {".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs"}
22
22
  _GO_EXTENSIONS = {".go"}
23
23
  _JVM_EXTENSIONS = {".java", ".kt", ".scala"}
24
+ _DOTNET_PROJECT_EXTENSIONS = {".csproj", ".fsproj", ".vbproj"}
24
25
  _SUPPORTED_EXTENSIONS = _PYTHON_EXTENSIONS | _NODE_EXTENSIONS | _GO_EXTENSIONS | _JVM_EXTENSIONS
25
26
  _DEFAULT_EDGE_KINDS: dict[GraphDetail, tuple[str, ...]] = {
26
27
  "high": ("imports",),
@@ -167,6 +168,23 @@ class GraphAnalyzer:
167
168
  go_imports = self._build_go_import_map(root, source_files)
168
169
  jvm_types = self._build_jvm_type_map(root, source_files)
169
170
 
171
+ # .NET: project-level graph from .csproj/.fsproj/.vbproj manifests
172
+ all_tree_paths = [p for p in flatten_file_tree(file_tree)]
173
+ dotnet_project_paths = [
174
+ p for p in all_tree_paths
175
+ if Path(p).suffix.lower() in self._DOTNET_PROJECT_EXTENSIONS
176
+ and (root / p).is_file()
177
+ ]
178
+ if dotnet_project_paths:
179
+ dn_nodes, dn_edges, dn_lims = self._analyze_dotnet_projects(
180
+ root, dotnet_project_paths, workspace
181
+ )
182
+ for node in dn_nodes:
183
+ self._append_node(nodes, node_ids, node)
184
+ for edge in dn_edges:
185
+ self._append_edge(edges, edge_keys, edge)
186
+ limitations.extend(dn_lims)
187
+
170
188
  for relative_path in source_files:
171
189
  absolute_path = root / relative_path
172
190
  try:
@@ -1144,6 +1162,62 @@ class GraphAnalyzer:
1144
1162
  edge_keys.add(key)
1145
1163
  edges.append(edge)
1146
1164
 
1165
+ def _analyze_dotnet_projects(
1166
+ self,
1167
+ root: Path,
1168
+ csproj_paths: list[str],
1169
+ workspace: str | None,
1170
+ ) -> tuple[list[GraphNode], list[GraphEdge], list[str]]:
1171
+ from sourcecode.detectors.csproj_parser import parse_csproj
1172
+
1173
+ nodes: list[GraphNode] = []
1174
+ edges: list[GraphEdge] = []
1175
+ limitations: list[str] = []
1176
+
1177
+ projects = []
1178
+ node_id_map: dict[str, str] = {} # csproj_path → node_id
1179
+
1180
+ for rel_path in csproj_paths:
1181
+ project = parse_csproj(root / rel_path, rel_path)
1182
+ if project is None:
1183
+ limitations.append(f"dotnet_parse_error:{rel_path}")
1184
+ continue
1185
+ projects.append(project)
1186
+ node_path = project.project_dir if project.project_dir else project.name
1187
+ node_id = f"module:{node_path}"
1188
+ node_id_map[project.path] = node_id
1189
+ nodes.append(
1190
+ GraphNode(
1191
+ id=node_id,
1192
+ kind="module",
1193
+ language=project.language,
1194
+ path=node_path,
1195
+ display_name=project.name,
1196
+ workspace=workspace,
1197
+ )
1198
+ )
1199
+
1200
+ for project in projects:
1201
+ source_id = node_id_map.get(project.path)
1202
+ if source_id is None:
1203
+ continue
1204
+ for ref_path in project.project_references:
1205
+ target_id = node_id_map.get(ref_path)
1206
+ if target_id is None:
1207
+ limitations.append(f"dotnet_unresolved_ref:{project.path}:{ref_path}")
1208
+ continue
1209
+ edges.append(
1210
+ GraphEdge(
1211
+ source=source_id,
1212
+ target=target_id,
1213
+ kind="imports",
1214
+ confidence="high",
1215
+ method="heuristic",
1216
+ )
1217
+ )
1218
+
1219
+ return nodes, edges, limitations
1220
+
1147
1221
  def _language_for_suffix(self, suffix: str) -> str:
1148
1222
  if suffix in self._PYTHON_EXTENSIONS:
1149
1223
  return "python"
@@ -1157,6 +1231,12 @@ class GraphAnalyzer:
1157
1231
  return "kotlin"
1158
1232
  if suffix == ".scala":
1159
1233
  return "scala"
1234
+ if suffix == ".cs":
1235
+ return "csharp"
1236
+ if suffix == ".fs":
1237
+ return "fsharp"
1238
+ if suffix == ".vb":
1239
+ return "vbnet"
1160
1240
  return "unknown"
1161
1241
 
1162
1242
  def _node_id(self, kind: str, path: str, symbol: str | None = None) -> str:
sourcecode/schema.py CHANGED
@@ -527,6 +527,6 @@ class SourceMap:
527
527
  code_notes: list[CodeNote] = field(default_factory=list)
528
528
  code_adrs: list[AdrRecord] = field(default_factory=list)
529
529
  code_notes_summary: Optional[CodeNotesSummary] = None
530
- # Confidence & Explainability (v0.23.0)
530
+ # Confidence & Explainability (v0.24.0)
531
531
  confidence_summary: Optional[ConfidenceSummary] = None
532
532
  analysis_gaps: list[AnalysisGap] = field(default_factory=list)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 0.23.0
3
+ Version: 0.24.0
4
4
  Summary: Genera un mapa de contexto estructurado de proyectos de software para agentes IA
5
5
  License: MIT
6
6
  Requires-Python: >=3.9
@@ -1,6 +1,6 @@
1
- sourcecode/__init__.py,sha256=Dh82QypoDBYj3Gur3QVPfjnJcgmk8S2TvsTDOr0Z0Mc,100
1
+ sourcecode/__init__.py,sha256=7ljM1Wbn_nskADHVqGmTI-Vmy84xpdNbAYtpFWq826E,100
2
2
  sourcecode/architecture_analyzer.py,sha256=zxgqeqn2FhIUDn06X-whjGKiXbrHFkRlzZcr2zKd0Gw,16874
3
- sourcecode/architecture_summary.py,sha256=rr-Q2LLe6bXZ27NuV2pYb0_LZyEFn3sngKMdXCZSGbE,13263
3
+ sourcecode/architecture_summary.py,sha256=EyCKVK_q0EZd1xtBODWEOLKKgMfvRw-UVEuo1Z4BRNI,15171
4
4
  sourcecode/classifier.py,sha256=Ft_RfYS-KOe0t7vjgUx04OoCJd1-DXK7k9-I0CFDSnU,6934
5
5
  sourcecode/cli.py,sha256=SyO5JHR0cKMoMEas-of1pnWneZMjr7JhTxURR7-Kv5w,30925
6
6
  sourcecode/code_notes_analyzer.py,sha256=rRd8bFYV0krjlxxQV0wenwE9K7pVpUQSR7KvSvUQKw4,9226
@@ -10,12 +10,12 @@ sourcecode/dependency_analyzer.py,sha256=Exq0BfInvfS5iAg9xAr6WI2uPNuotkIudTKcYJc
10
10
  sourcecode/doc_analyzer.py,sha256=Ec3orx6vBKsh5cNM3-F4y2Got2KuKx8w3dErwtdtM-A,19891
11
11
  sourcecode/env_analyzer.py,sha256=JZxBOuIxnM0xw0IaJFL6LiPJBErL848L3XoTOHGBqZI,13554
12
12
  sourcecode/git_analyzer.py,sha256=S9PGt8RDasBQYQUsZh9-H_KWVlPvzwR4jgM7TKN9ZCk,7643
13
- sourcecode/graph_analyzer.py,sha256=cxl7Xb88aGxIVOCsatZJAE1CtgXGzIbJfqytV-9kkFs,45172
13
+ sourcecode/graph_analyzer.py,sha256=t5D1MpoGeyINol9UK3cQyn0e79q7cvC5MLc2p4JWXhI,48132
14
14
  sourcecode/metrics_analyzer.py,sha256=4uh11v-Q0gdrN87BOxuFWUym3N3AOkOuy21K5N8peB8,20126
15
15
  sourcecode/prepare_context.py,sha256=1Z8UcefgIWu1J_tYXVIu2qNbTt08S72gW7QUe9c7u0s,27561
16
16
  sourcecode/redactor.py,sha256=xuGcadGEHaPw4qZXlMDvzMCsr4VOkdp3oBQptHyJk8c,2884
17
17
  sourcecode/scanner.py,sha256=aM3h9-DCQ3xKpeHpHYdo2vX6T5P95HA_YwZbkAVNwmo,8288
18
- sourcecode/schema.py,sha256=u8WorLBlbcG-I5oaB0ShTGHDoNaLcl4vyADNV6P2O9g,17159
18
+ sourcecode/schema.py,sha256=FaXyEKYoYEE8YCpay8u2k5CfW0-79PV0HNwpvSRq-Pc,17159
19
19
  sourcecode/semantic_analyzer.py,sha256=asQfJf-EhzYaOTA-iMuZsrVXtbW7SV2WEKCxgsxa88Y,79413
20
20
  sourcecode/serializer.py,sha256=-dNeUFingePa3WcCTjLe0o6jSdLu4PfL9rPlD8uuky4,25163
21
21
  sourcecode/summarizer.py,sha256=YdIA5gGEc0XSctPgmTnFnuZI72bprrOYhkJNoDmvHwM,12114
@@ -23,8 +23,9 @@ sourcecode/tree_utils.py,sha256=Fj9OIuUksBvgibNd3feog0sMDjVypJzPexp5lvMoYWI,1424
23
23
  sourcecode/workspace.py,sha256=fQlVoNx8S-fSHpKoJ0JBvEHCFkxszH0KZVJed1i3TRk,6845
24
24
  sourcecode/detectors/__init__.py,sha256=A0AACJFF6HWf_RgatNtWu3PUzstcKtIGM9f1PoFcJug,1987
25
25
  sourcecode/detectors/base.py,sha256=C2EqfZudQ1ITK4DID4M70nPxqoh9bl1zn_ta6XRaGWs,1168
26
+ sourcecode/detectors/csproj_parser.py,sha256=2Vxfo6h_ucF1KPTTOYLzNnCGRfHWIATVMeaHY45PbQY,6756
26
27
  sourcecode/detectors/dart.py,sha256=QbqaL5v18-_ort75HihVBt8MsKUfOcFDF8IpWFLiXpI,1432
27
- sourcecode/detectors/dotnet.py,sha256=UC-74VW850r3admcl36R-rl3M0-nBFXw8ph5e9RSas8,1978
28
+ sourcecode/detectors/dotnet.py,sha256=p44WohTC8a6Xwxa5mUK0FRM3aT8tOotckmDnYgAqbZ8,6493
28
29
  sourcecode/detectors/elixir.py,sha256=jCpvt5Yi6jvplc80ovRtWh17q-11ZGo9qX7o8b57TJE,1713
29
30
  sourcecode/detectors/go.py,sha256=KehAyvC3H_s8zwwqFM0bAbztQbbUBDjpNbGvM2kQlk8,1836
30
31
  sourcecode/detectors/heuristic.py,sha256=JWb_dXNI1YDxk9a3DFeXTbKYfAGOFqV6jVxxqouWSiA,2238
@@ -40,7 +41,7 @@ sourcecode/detectors/rust.py,sha256=V23NO6Y8NsrhrUlGC2feUEcNK63hf3gc1y3jI4tes1Q,
40
41
  sourcecode/detectors/systems.py,sha256=nYaKbGDFu0EOXFcd_1doWFT3tTUdkbxc2DjHUF5TcqQ,1627
41
42
  sourcecode/detectors/terraform.py,sha256=cxORPR_zVLOJpHlh4e9JnFpkQsn_UnqMMom5yG65hZ4,1693
42
43
  sourcecode/detectors/tooling.py,sha256=hIvop80No22pqyGVJ32NKliSdjkHRePQkIRroqG01bY,1875
43
- sourcecode-0.23.0.dist-info/METADATA,sha256=EwoOCYdt8LinpV5ymkOsnd3A01aEcFxbeidutgfwPsw,30326
44
- sourcecode-0.23.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
45
- sourcecode-0.23.0.dist-info/entry_points.txt,sha256=voDtdlGU2-jR3wCZpQoOls_0eUKaDVCKIfajGHFDATs,50
46
- sourcecode-0.23.0.dist-info/RECORD,,
44
+ sourcecode-0.24.0.dist-info/METADATA,sha256=6zBHS6C-B7sxfs54ThpVQBcEMnGvohExI-hy69n2dRQ,30326
45
+ sourcecode-0.24.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
46
+ sourcecode-0.24.0.dist-info/entry_points.txt,sha256=voDtdlGU2-jR3wCZpQoOls_0eUKaDVCKIfajGHFDATs,50
47
+ sourcecode-0.24.0.dist-info/RECORD,,