sourcecode 0.2.0__py3-none-any.whl → 0.4.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.2.0"
3
+ __version__ = "0.4.0"
sourcecode/cli.py CHANGED
@@ -46,6 +46,16 @@ def main(
46
46
  "--compact",
47
47
  help="Output reducido (~500 tokens): tipo de proyecto, stacks, entradas y arbol nivel 1",
48
48
  ),
49
+ dependencies: bool = typer.Option(
50
+ False,
51
+ "--dependencies",
52
+ help="Incluir dependencias directas, versiones exactas y transitivas cuando haya lockfiles compatibles",
53
+ ),
54
+ graph_modules: bool = typer.Option(
55
+ False,
56
+ "--graph-modules",
57
+ help="Incluir grafo estructural de modulos, imports y relaciones simples del codigo",
58
+ ),
49
59
  no_redact: bool = typer.Option(
50
60
  False,
51
61
  "--no-redact",
@@ -88,7 +98,9 @@ def main(
88
98
  # --- Importar modulos de logica ---
89
99
  from dataclasses import asdict, replace
90
100
 
101
+ from sourcecode.dependency_analyzer import DependencyAnalyzer
91
102
  from sourcecode.detectors import ProjectDetector, build_default_detectors
103
+ from sourcecode.graph_analyzer import GraphAnalyzer
92
104
  from sourcecode.redactor import SecretRedactor, redact_dict
93
105
  from sourcecode.scanner import FileScanner
94
106
  from sourcecode.schema import AnalysisMetadata, EntryPoint, SourceMap, StackDetection
@@ -112,10 +124,33 @@ def main(
112
124
  filtered[name] = value
113
125
  return filtered
114
126
 
127
+ def prune_workspace_paths(
128
+ tree: dict[str, Any], workspace_paths: list[str]
129
+ ) -> dict[str, Any]:
130
+ pruned = dict(tree)
131
+ for workspace_path in workspace_paths:
132
+ parts = [part for part in workspace_path.split("/") if part]
133
+ if not parts:
134
+ continue
135
+ node = pruned
136
+ for index, part in enumerate(parts):
137
+ if not isinstance(node, dict) or part not in node:
138
+ break
139
+ if index == len(parts) - 1:
140
+ node.pop(part, None)
141
+ break
142
+ child = node.get(part)
143
+ if not isinstance(child, dict):
144
+ break
145
+ node = child
146
+ return pruned
147
+
115
148
  file_tree = filter_sensitive_files(raw_tree)
116
149
  manifests = scanner.find_manifests()
117
150
  detector = ProjectDetector(build_default_detectors())
118
151
  workspace_analysis = WorkspaceAnalyzer().analyze(target, manifests)
152
+ dependency_analyzer = DependencyAnalyzer() if dependencies else None
153
+ graph_analyzer = GraphAnalyzer() if graph_modules else None
119
154
 
120
155
  root_manifests = [
121
156
  manifest
@@ -129,6 +164,24 @@ def main(
129
164
  else:
130
165
  stacks, entry_points, _project_type = detector.detect(target, file_tree, detection_manifests)
131
166
 
167
+ dependency_records = []
168
+ dependency_summaries = []
169
+ if dependency_analyzer is not None:
170
+ root_dependencies, root_summary = dependency_analyzer.analyze(target)
171
+ dependency_records.extend(root_dependencies)
172
+ dependency_summaries.append(root_summary)
173
+ module_graphs = []
174
+ if graph_analyzer is not None:
175
+ root_graph_tree = (
176
+ prune_workspace_paths(
177
+ file_tree,
178
+ [workspace.path for workspace in workspace_analysis.workspaces],
179
+ )
180
+ if workspace_analysis.workspaces
181
+ else file_tree
182
+ )
183
+ module_graphs.append(graph_analyzer.analyze(target, root_graph_tree))
184
+
132
185
  for workspace in workspace_analysis.workspaces:
133
186
  workspace_root = target / workspace.path
134
187
  if not workspace_root.exists() or not workspace_root.is_dir():
@@ -153,6 +206,22 @@ def main(
153
206
  )
154
207
  for entry_point in workspace_entry_points
155
208
  )
209
+ if dependency_analyzer is not None:
210
+ workspace_dependencies, workspace_summary = dependency_analyzer.analyze(
211
+ workspace_root,
212
+ workspace=workspace.path,
213
+ )
214
+ dependency_records.extend(workspace_dependencies)
215
+ dependency_summaries.append(workspace_summary)
216
+ if graph_analyzer is not None:
217
+ workspace_graph = graph_analyzer.analyze(
218
+ workspace_root,
219
+ workspace_tree,
220
+ workspace=workspace.path,
221
+ )
222
+ module_graphs.append(
223
+ graph_analyzer.prefix_graph(workspace_graph, workspace.path, workspace.path)
224
+ )
156
225
 
157
226
  stacks, project_type = detector.classify_results(
158
227
  file_tree,
@@ -160,6 +229,12 @@ def main(
160
229
  entry_points,
161
230
  project_type_override="monorepo" if workspace_analysis.is_monorepo else None,
162
231
  )
232
+ dependency_summary = (
233
+ dependency_analyzer.merge_summaries(dependency_summaries)
234
+ if dependency_analyzer is not None
235
+ else None
236
+ )
237
+ module_graph = graph_analyzer.merge_graphs(module_graphs) if graph_analyzer is not None else None
163
238
 
164
239
  # 3. Construir el schema
165
240
  metadata = AnalysisMetadata(analyzed_path=str(target))
@@ -169,6 +244,9 @@ def main(
169
244
  stacks=stacks,
170
245
  project_type=project_type,
171
246
  entry_points=entry_points,
247
+ dependencies=dependency_records,
248
+ dependency_summary=dependency_summary,
249
+ module_graph=module_graph,
172
250
  )
173
251
 
174
252
  # 4. Serializar (con o sin modo compact)