sourcecode 0.9.0__tar.gz → 0.10.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.
- {sourcecode-0.9.0 → sourcecode-0.10.0}/PKG-INFO +7 -6
- {sourcecode-0.9.0 → sourcecode-0.10.0}/README.md +6 -5
- {sourcecode-0.9.0 → sourcecode-0.10.0}/docs/schema.md +12 -11
- {sourcecode-0.9.0 → sourcecode-0.10.0}/pyproject.toml +1 -1
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/__init__.py +1 -1
- sourcecode-0.10.0/src/sourcecode/architecture_summary.py +248 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/cli.py +7 -5
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/detectors/go.py +7 -1
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/detectors/nodejs.py +30 -11
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/detectors/python.py +28 -6
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/schema.py +2 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/serializer.py +2 -2
- sourcecode-0.10.0/src/sourcecode/summarizer.py +177 -0
- sourcecode-0.10.0/tests/test_architecture_summary.py +72 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_detector_go_rust_java.py +2 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_detector_nodejs.py +24 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_detector_python.py +22 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_detectors_base.py +1 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_integration_lqn.py +21 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_schema.py +22 -12
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_summarizer.py +47 -0
- sourcecode-0.9.0/src/sourcecode/summarizer.py +0 -68
- {sourcecode-0.9.0 → sourcecode-0.10.0}/.gitignore +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/.ruff.toml +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/classifier.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/coverage_parser.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/dependency_analyzer.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/detectors/__init__.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/detectors/base.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/detectors/dart.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/detectors/dotnet.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/detectors/elixir.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/detectors/heuristic.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/detectors/java.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/detectors/parsers.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/detectors/php.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/detectors/project.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/detectors/ruby.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/detectors/rust.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/detectors/systems.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/detectors/terraform.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/detectors/tooling.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/doc_analyzer.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/graph_analyzer.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/metrics_analyzer.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/redactor.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/scanner.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/semantic_analyzer.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/tree_utils.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/src/sourcecode/workspace.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/__init__.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/conftest.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/fixtures/coverage.xml +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/fixtures/fastapi_app/pyproject.toml +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/fixtures/fastapi_app/src/main.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/fixtures/go_service/cmd/api/main.go +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/fixtures/go_service/go.mod +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/fixtures/jacoco.xml +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/fixtures/lcov.info +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/fixtures/nextjs_app/app/page.tsx +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/fixtures/nextjs_app/package.json +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/fixtures/nextjs_app/pnpm-lock.yaml +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/fixtures/pnpm_monorepo/apps/web/app/page.tsx +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/fixtures/pnpm_monorepo/apps/web/package.json +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/fixtures/pnpm_monorepo/packages/api/main.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/fixtures/pnpm_monorepo/packages/api/pyproject.toml +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/fixtures/pnpm_monorepo/pnpm-workspace.yaml +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_classifier.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_cli.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_coverage_parser.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_dependency_analyzer_node_python.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_dependency_analyzer_polyglot.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_dependency_schema.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_detector_php_ruby_dart.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_detector_universal_managed.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_detector_universal_systems.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_doc_analyzer_jsdom.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_doc_analyzer_python.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_graph_analyzer_polyglot.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_graph_analyzer_python_node.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_graph_schema.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_integration.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_integration_dependencies.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_integration_detection.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_integration_docs.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_integration_graph_modules.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_integration_metrics.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_integration_multistack.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_integration_semantics.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_integration_universal.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_metrics_analyzer.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_packaging.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_real_projects.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_redactor.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_scanner.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_semantic_analyzer_node.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_semantic_analyzer_python.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_semantic_import_resolution.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.0}/tests/test_semantic_schema.py +0 -0
- {sourcecode-0.9.0 → sourcecode-0.10.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.
|
|
3
|
+
Version: 0.10.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
|
|
@@ -94,7 +94,7 @@ sourcecode --version
|
|
|
94
94
|
| `PATH` | `.` | Directory to analyze. |
|
|
95
95
|
| `--format json\|yaml` | `json` | Output format. |
|
|
96
96
|
| `--output PATH` | stdout | Write to a file instead of stdout. |
|
|
97
|
-
| `--compact` | off | Reduced output (~500-700 tokens): `schema_version`, `project_type`, `project_summary`, `
|
|
97
|
+
| `--compact` | off | Reduced output (~500-700 tokens): `schema_version`, `project_type`, `project_summary`, `architecture_summary`, `stacks`, `entry_points`, `file_tree_depth1`, and `dependency_summary` when available. |
|
|
98
98
|
| `--dependencies` | off | Include direct dependencies, resolved versions, and transitive relationships when lockfiles make that possible. Also populates `key_dependencies`. |
|
|
99
99
|
| `--graph-modules` | off | Include a structural module graph with imports and simple relations. |
|
|
100
100
|
| `--graph-detail high\|medium\|full` | `high` | Graph detail level: summarized (high), balanced (medium), or full-fidelity (full). |
|
|
@@ -118,6 +118,7 @@ The full schema (`SourceMap`) includes the following fields:
|
|
|
118
118
|
| `file_tree` | object | Repository tree where `null` represents a file and `{}` represents a directory. |
|
|
119
119
|
| `file_paths` | array | Flat list of all project paths derived from `file_tree`, with forward-slash separators. Always present; respects `--depth`. |
|
|
120
120
|
| `project_summary` | string\|null | Deterministic natural-language description of the project generated from detected stacks, entry points, and dependencies. Present when stacks are detected. |
|
|
121
|
+
| `architecture_summary` | string\|null | Static summary of the main execution flow, orchestrated modules, and output produced by the project. Present when enough structural evidence is available. |
|
|
121
122
|
| `stacks` | array | Stack detections with confidence, frameworks, manifests, `primary`, `root`, `workspace`, and `signals`. |
|
|
122
123
|
| `project_type` | string\|null | Overall project classification. |
|
|
123
124
|
| `entry_points` | array | Detected entry points by stack. |
|
|
@@ -155,6 +156,7 @@ Real output from a Python FastAPI project:
|
|
|
155
156
|
"schema_version": "1.0",
|
|
156
157
|
"project_type": "api",
|
|
157
158
|
"project_summary": "API en Python (FastAPI). Entry points: src/main.py. 12 dependencias (python).",
|
|
159
|
+
"architecture_summary": null,
|
|
158
160
|
"stacks": [
|
|
159
161
|
{
|
|
160
162
|
"stack": "python",
|
|
@@ -179,7 +181,6 @@ Real output from a Python FastAPI project:
|
|
|
179
181
|
"source": "manifest"
|
|
180
182
|
}
|
|
181
183
|
],
|
|
182
|
-
"file_paths": ["pyproject.toml", "src/main.py", "src/routes.py", "tests/test_main.py"],
|
|
183
184
|
"file_tree_depth1": {
|
|
184
185
|
"pyproject.toml": null,
|
|
185
186
|
"src": {},
|
|
@@ -371,7 +372,7 @@ Different modes optimize for different tradeoffs between context size and depth
|
|
|
371
372
|
|
|
372
373
|
Best for: initial orientation, deciding what to explore next, fast handoffs between agents.
|
|
373
374
|
|
|
374
|
-
Includes: `project_summary` (instant project description), `
|
|
375
|
+
Includes: `project_summary` (instant project description), `architecture_summary` (execution-oriented static summary), `stacks`, `entry_points`, `file_tree_depth1`, and `dependency_summary` when `--dependencies` was also requested.
|
|
375
376
|
|
|
376
377
|
```bash
|
|
377
378
|
sourcecode --compact .
|
|
@@ -404,9 +405,9 @@ sourcecode --docs --docs-depth full . # include methods
|
|
|
404
405
|
- `"Aplicacion web en Node.js (Next.js, React). Entry points: app/page.tsx."`
|
|
405
406
|
- `"Monorepo con 2 workspaces en Node.js, Python."`
|
|
406
407
|
|
|
407
|
-
**`
|
|
408
|
+
**`architecture_summary` field**
|
|
408
409
|
|
|
409
|
-
`
|
|
410
|
+
`architecture_summary` is a static 3-5 line summary oriented to execution flow. It answers what the main entry point does, which modules it orchestrates, and what the project produces. In compact mode it replaces the low-signal value that `file_paths` used to occupy.
|
|
410
411
|
|
|
411
412
|
**`key_dependencies` field**
|
|
412
413
|
|
|
@@ -78,7 +78,7 @@ sourcecode --version
|
|
|
78
78
|
| `PATH` | `.` | Directory to analyze. |
|
|
79
79
|
| `--format json\|yaml` | `json` | Output format. |
|
|
80
80
|
| `--output PATH` | stdout | Write to a file instead of stdout. |
|
|
81
|
-
| `--compact` | off | Reduced output (~500-700 tokens): `schema_version`, `project_type`, `project_summary`, `
|
|
81
|
+
| `--compact` | off | Reduced output (~500-700 tokens): `schema_version`, `project_type`, `project_summary`, `architecture_summary`, `stacks`, `entry_points`, `file_tree_depth1`, and `dependency_summary` when available. |
|
|
82
82
|
| `--dependencies` | off | Include direct dependencies, resolved versions, and transitive relationships when lockfiles make that possible. Also populates `key_dependencies`. |
|
|
83
83
|
| `--graph-modules` | off | Include a structural module graph with imports and simple relations. |
|
|
84
84
|
| `--graph-detail high\|medium\|full` | `high` | Graph detail level: summarized (high), balanced (medium), or full-fidelity (full). |
|
|
@@ -102,6 +102,7 @@ The full schema (`SourceMap`) includes the following fields:
|
|
|
102
102
|
| `file_tree` | object | Repository tree where `null` represents a file and `{}` represents a directory. |
|
|
103
103
|
| `file_paths` | array | Flat list of all project paths derived from `file_tree`, with forward-slash separators. Always present; respects `--depth`. |
|
|
104
104
|
| `project_summary` | string\|null | Deterministic natural-language description of the project generated from detected stacks, entry points, and dependencies. Present when stacks are detected. |
|
|
105
|
+
| `architecture_summary` | string\|null | Static summary of the main execution flow, orchestrated modules, and output produced by the project. Present when enough structural evidence is available. |
|
|
105
106
|
| `stacks` | array | Stack detections with confidence, frameworks, manifests, `primary`, `root`, `workspace`, and `signals`. |
|
|
106
107
|
| `project_type` | string\|null | Overall project classification. |
|
|
107
108
|
| `entry_points` | array | Detected entry points by stack. |
|
|
@@ -139,6 +140,7 @@ Real output from a Python FastAPI project:
|
|
|
139
140
|
"schema_version": "1.0",
|
|
140
141
|
"project_type": "api",
|
|
141
142
|
"project_summary": "API en Python (FastAPI). Entry points: src/main.py. 12 dependencias (python).",
|
|
143
|
+
"architecture_summary": null,
|
|
142
144
|
"stacks": [
|
|
143
145
|
{
|
|
144
146
|
"stack": "python",
|
|
@@ -163,7 +165,6 @@ Real output from a Python FastAPI project:
|
|
|
163
165
|
"source": "manifest"
|
|
164
166
|
}
|
|
165
167
|
],
|
|
166
|
-
"file_paths": ["pyproject.toml", "src/main.py", "src/routes.py", "tests/test_main.py"],
|
|
167
168
|
"file_tree_depth1": {
|
|
168
169
|
"pyproject.toml": null,
|
|
169
170
|
"src": {},
|
|
@@ -355,7 +356,7 @@ Different modes optimize for different tradeoffs between context size and depth
|
|
|
355
356
|
|
|
356
357
|
Best for: initial orientation, deciding what to explore next, fast handoffs between agents.
|
|
357
358
|
|
|
358
|
-
Includes: `project_summary` (instant project description), `
|
|
359
|
+
Includes: `project_summary` (instant project description), `architecture_summary` (execution-oriented static summary), `stacks`, `entry_points`, `file_tree_depth1`, and `dependency_summary` when `--dependencies` was also requested.
|
|
359
360
|
|
|
360
361
|
```bash
|
|
361
362
|
sourcecode --compact .
|
|
@@ -388,9 +389,9 @@ sourcecode --docs --docs-depth full . # include methods
|
|
|
388
389
|
- `"Aplicacion web en Node.js (Next.js, React). Entry points: app/page.tsx."`
|
|
389
390
|
- `"Monorepo con 2 workspaces en Node.js, Python."`
|
|
390
391
|
|
|
391
|
-
**`
|
|
392
|
+
**`architecture_summary` field**
|
|
392
393
|
|
|
393
|
-
`
|
|
394
|
+
`architecture_summary` is a static 3-5 line summary oriented to execution flow. It answers what the main entry point does, which modules it orchestrates, and what the project produces. In compact mode it replaces the low-signal value that `file_paths` used to occupy.
|
|
394
395
|
|
|
395
396
|
**`key_dependencies` field**
|
|
396
397
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
`sourcecode` serializa un objeto `SourceMap` con schema `1.0`.
|
|
5
5
|
|
|
6
|
-
Los campos **siempre presentes** son: `metadata`, `file_tree`, `file_paths`, `stacks`, `project_type`, `entry_points` y `
|
|
6
|
+
Los campos **siempre presentes** son: `metadata`, `file_tree`, `file_paths`, `stacks`, `project_type`, `entry_points`, `project_summary` y `architecture_summary`.
|
|
7
7
|
|
|
8
8
|
Los campos **opcionales** dependen de los flags activos en la invocacion:
|
|
9
9
|
|
|
@@ -28,6 +28,7 @@ Los campos **opcionales** dependen de los flags activos en la invocacion:
|
|
|
28
28
|
"project_type": "webapp",
|
|
29
29
|
"entry_points": [],
|
|
30
30
|
"project_summary": "Aplicacion web en Nodejs (Next.js). Entry points: app/page.tsx.",
|
|
31
|
+
"architecture_summary": null,
|
|
31
32
|
"dependencies": [],
|
|
32
33
|
"dependency_summary": null,
|
|
33
34
|
"key_dependencies": [],
|
|
@@ -47,6 +48,7 @@ Campos:
|
|
|
47
48
|
- `project_type`: clasificacion general del proyecto. Siempre presente (puede ser `null`).
|
|
48
49
|
- `entry_points`: puntos de entrada relevantes. Siempre presente (puede ser lista vacia).
|
|
49
50
|
- `project_summary`: descripcion en lenguaje natural del proyecto generada deterministicamente. Presente cuando hay stacks detectados; `null` si no (Phase 9).
|
|
51
|
+
- `architecture_summary`: resumen arquitectonico estatico del flujo principal, modulos orquestados y output producido. `null` si no hay evidencia suficiente (Phase 13).
|
|
50
52
|
- `dependencies`: dependencias detectadas. Solo presente con `--dependencies`; lista vacia por defecto.
|
|
51
53
|
- `dependency_summary`: resumen del analisis de dependencias. Solo presente con `--dependencies`; `null` por defecto.
|
|
52
54
|
- `key_dependencies`: top-15 dependencias directas relevantes. Solo presente con `--dependencies`; lista vacia por defecto (Phase 9).
|
|
@@ -61,7 +63,7 @@ Campos:
|
|
|
61
63
|
{
|
|
62
64
|
"schema_version": "1.0",
|
|
63
65
|
"generated_at": "2026-04-07T19:41:05.686277+00:00",
|
|
64
|
-
"sourcecode_version": "0.
|
|
66
|
+
"sourcecode_version": "0.10.0",
|
|
65
67
|
"analyzed_path": "/abs/path/to/project"
|
|
66
68
|
}
|
|
67
69
|
```
|
|
@@ -232,7 +234,8 @@ Cada elemento de `entry_points` sigue este shape:
|
|
|
232
234
|
"path": "app/page.tsx",
|
|
233
235
|
"stack": "nodejs",
|
|
234
236
|
"kind": "web",
|
|
235
|
-
"source": "package.json"
|
|
237
|
+
"source": "package.json",
|
|
238
|
+
"confidence": "high"
|
|
236
239
|
}
|
|
237
240
|
```
|
|
238
241
|
|
|
@@ -242,6 +245,7 @@ Campos:
|
|
|
242
245
|
- `stack`: stack al que pertenece.
|
|
243
246
|
- `kind`: tipo de entry point, por ejemplo `web`, `api`, `cli` o `entry`.
|
|
244
247
|
- `source`: origen de la deteccion.
|
|
248
|
+
- `confidence`: confianza del entry point detectado (`high`, `medium` o `low`).
|
|
245
249
|
|
|
246
250
|
## dependencies
|
|
247
251
|
|
|
@@ -536,13 +540,9 @@ Con `--compact`, la salida omite `metadata`, el arbol completo, `dependencies`,
|
|
|
536
540
|
"schema_version": "1.0",
|
|
537
541
|
"project_type": "webapp",
|
|
538
542
|
"project_summary": "Aplicacion web en Nodejs (Next.js, React). Entry points: app/page.tsx.",
|
|
543
|
+
"architecture_summary": null,
|
|
539
544
|
"stacks": [],
|
|
540
545
|
"entry_points": [],
|
|
541
|
-
"file_paths": [
|
|
542
|
-
"package.json",
|
|
543
|
-
"app/page.tsx",
|
|
544
|
-
"app/layout.tsx"
|
|
545
|
-
],
|
|
546
546
|
"file_tree_depth1": {
|
|
547
547
|
"package.json": null,
|
|
548
548
|
"app": {}
|
|
@@ -556,16 +556,17 @@ Campos incluidos en el modo compacto:
|
|
|
556
556
|
- `schema_version`: version del schema.
|
|
557
557
|
- `project_type`: clasificacion general.
|
|
558
558
|
- `project_summary`: descripcion NL del proyecto. Siempre incluido (Phase 9).
|
|
559
|
+
- `architecture_summary`: resumen arquitectonico estatico del flujo principal. Siempre incluido; puede ser `null` si la evidencia es insuficiente (Phase 13).
|
|
559
560
|
- `stacks`: stacks detectados serializados.
|
|
560
561
|
- `entry_points`: entry points serializados.
|
|
561
|
-
- `file_paths`: lista plana de todos los paths con separador forward-slash. Siempre incluido (Phase 9).
|
|
562
562
|
- `file_tree_depth1`: solo el primer nivel del `file_tree`. Se conserva por compatibilidad retroactiva.
|
|
563
563
|
- `dependency_summary`: resumen de dependencias cuando `--dependencies` esta activo y `dependency_summary.requested == True`; `null` en cualquier otro caso (Phase 9).
|
|
564
564
|
|
|
565
565
|
Campos **excluidos** en modo compacto aunque se combinen con otros flags:
|
|
566
566
|
|
|
567
567
|
- `metadata`
|
|
568
|
-
- `file_tree` (sustituido por `file_tree_depth1`
|
|
568
|
+
- `file_tree` (sustituido por `file_tree_depth1`)
|
|
569
|
+
- `file_paths`
|
|
569
570
|
- `dependencies`
|
|
570
571
|
- `key_dependencies`
|
|
571
572
|
- `module_graph`
|
|
@@ -582,7 +583,7 @@ Ejemplo de salida para un monorepo con web Node.js y API Python con `--dependenc
|
|
|
582
583
|
"metadata": {
|
|
583
584
|
"schema_version": "1.0",
|
|
584
585
|
"generated_at": "2026-04-07T19:41:05.686277+00:00",
|
|
585
|
-
"sourcecode_version": "0.
|
|
586
|
+
"sourcecode_version": "0.10.0",
|
|
586
587
|
"analyzed_path": "/abs/path/to/project"
|
|
587
588
|
},
|
|
588
589
|
"file_tree": {
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"""Resumen arquitectonico estatico y compacto para consumo LLM."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import ast
|
|
5
|
+
import re
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from sourcecode.schema import EntryPoint, SourceMap, StackDetection
|
|
10
|
+
from sourcecode.tree_utils import flatten_file_tree
|
|
11
|
+
|
|
12
|
+
_TOOLING_PREFIXES = (".claude/", ".vscode/", "bin/")
|
|
13
|
+
_PYTHON_EXTENSIONS = {".py"}
|
|
14
|
+
_NODE_EXTENSIONS = {".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs"}
|
|
15
|
+
_GO_EXTENSIONS = {".go"}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ArchitectureSummarizer:
|
|
19
|
+
"""Construye un resumen arquitectonico estatico de 3-5 lineas."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, root: Path) -> None:
|
|
22
|
+
self.root = root
|
|
23
|
+
|
|
24
|
+
def generate(self, sm: SourceMap) -> str | None:
|
|
25
|
+
try:
|
|
26
|
+
return self._build_summary(sm)
|
|
27
|
+
except Exception:
|
|
28
|
+
return None
|
|
29
|
+
|
|
30
|
+
def _build_summary(self, sm: SourceMap) -> str | None:
|
|
31
|
+
file_paths = [
|
|
32
|
+
path for path in flatten_file_tree(sm.file_tree)
|
|
33
|
+
if not self._is_tooling_path(path)
|
|
34
|
+
]
|
|
35
|
+
if not file_paths:
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
entry_points = [
|
|
39
|
+
entry for entry in sm.entry_points
|
|
40
|
+
if not self._is_tooling_path(entry.path)
|
|
41
|
+
]
|
|
42
|
+
if not entry_points:
|
|
43
|
+
fallback = self._infer_fallback_entry_points(file_paths, sm.stacks)
|
|
44
|
+
entry_points = fallback[:1]
|
|
45
|
+
|
|
46
|
+
if not entry_points:
|
|
47
|
+
return "Arquitectura no inferida con suficiente evidencia estatica."
|
|
48
|
+
|
|
49
|
+
entry_point = entry_points[0]
|
|
50
|
+
content = self._read_file(entry_point.path)
|
|
51
|
+
if content is None:
|
|
52
|
+
return f"Entry point principal: {entry_point.path}. Arquitectura no inferida con suficiente evidencia estatica."
|
|
53
|
+
|
|
54
|
+
lines = [self._describe_entry_point(entry_point, sm.project_type)]
|
|
55
|
+
suffix = Path(entry_point.path).suffix
|
|
56
|
+
if suffix in _PYTHON_EXTENSIONS:
|
|
57
|
+
lines.extend(self._summarize_python_entry(entry_point.path, content))
|
|
58
|
+
elif suffix in _NODE_EXTENSIONS:
|
|
59
|
+
lines.extend(self._summarize_node_entry(entry_point.path, content))
|
|
60
|
+
elif suffix in _GO_EXTENSIONS:
|
|
61
|
+
lines.extend(self._summarize_go_entry(entry_point.path, content))
|
|
62
|
+
else:
|
|
63
|
+
lines.append("Orquesta modulos internos no detallados por el analisis estatico disponible.")
|
|
64
|
+
|
|
65
|
+
unique_lines: list[str] = []
|
|
66
|
+
seen: set[str] = set()
|
|
67
|
+
for line in lines:
|
|
68
|
+
line = line.strip()
|
|
69
|
+
if not line or line in seen:
|
|
70
|
+
continue
|
|
71
|
+
seen.add(line)
|
|
72
|
+
unique_lines.append(line)
|
|
73
|
+
return "\n".join(unique_lines[:5]) if unique_lines else None
|
|
74
|
+
|
|
75
|
+
def _summarize_python_entry(self, path: str, content: str) -> list[str]:
|
|
76
|
+
try:
|
|
77
|
+
tree = ast.parse(content)
|
|
78
|
+
except SyntaxError:
|
|
79
|
+
return ["Orquesta modulos Python, pero el entry point no pudo parsearse por completo."]
|
|
80
|
+
|
|
81
|
+
package_prefix = self._python_package_prefix(path)
|
|
82
|
+
imported_modules: list[str] = []
|
|
83
|
+
optional_analyzers: list[str] = []
|
|
84
|
+
uses_serializer = False
|
|
85
|
+
uses_redactor = False
|
|
86
|
+
|
|
87
|
+
for node in ast.walk(tree):
|
|
88
|
+
if isinstance(node, ast.ImportFrom) and isinstance(node.module, str):
|
|
89
|
+
if package_prefix and node.module.startswith(package_prefix):
|
|
90
|
+
imported_modules.append(node.module.rsplit(".", 1)[-1])
|
|
91
|
+
if node.module == "sourcecode.serializer":
|
|
92
|
+
uses_serializer = True
|
|
93
|
+
if node.module == "sourcecode.redactor":
|
|
94
|
+
uses_redactor = True
|
|
95
|
+
elif isinstance(node, ast.Assign):
|
|
96
|
+
optional = self._extract_optional_analyzer(node)
|
|
97
|
+
if optional:
|
|
98
|
+
optional_analyzers.append(optional)
|
|
99
|
+
|
|
100
|
+
lines: list[str] = []
|
|
101
|
+
core_modules = self._format_module_list(imported_modules)
|
|
102
|
+
if core_modules:
|
|
103
|
+
lines.append(f"Orquesta modulos internos: {core_modules}.")
|
|
104
|
+
if optional_analyzers:
|
|
105
|
+
lines.append(f"Activa analisis opcionales: {', '.join(optional_analyzers)}.")
|
|
106
|
+
if uses_serializer:
|
|
107
|
+
output_line = "Produce un SourceMap serializado en JSON/YAML y una vista compacta."
|
|
108
|
+
if uses_redactor:
|
|
109
|
+
output_line = "Produce un SourceMap serializado en JSON/YAML y una vista compacta con redaccion de secretos."
|
|
110
|
+
lines.append(output_line)
|
|
111
|
+
elif not lines:
|
|
112
|
+
lines.append("Orquesta modulos Python internos sin un patron de salida claramente inferible.")
|
|
113
|
+
return lines
|
|
114
|
+
|
|
115
|
+
def _summarize_node_entry(self, path: str, content: str) -> list[str]:
|
|
116
|
+
imports = re.findall(r"""from\s+['"](\.?\.?/[^'"]+)['"]|require\(['"](\.?\.?/[^'"]+)['"]\)""", content)
|
|
117
|
+
modules = [item for pair in imports for item in pair if item]
|
|
118
|
+
lines: list[str] = []
|
|
119
|
+
if modules:
|
|
120
|
+
formatted = self._format_module_list([self._module_label(module) for module in modules])
|
|
121
|
+
if formatted:
|
|
122
|
+
lines.append(f"Orquesta modulos internos: {formatted}.")
|
|
123
|
+
lines.append("Produce la salida principal del entry point JavaScript/TypeScript detectado.")
|
|
124
|
+
return lines
|
|
125
|
+
|
|
126
|
+
def _summarize_go_entry(self, path: str, content: str) -> list[str]:
|
|
127
|
+
imports = re.findall(r'"([^"]+)"', content)
|
|
128
|
+
internal = [module for module in imports if not module.startswith(("fmt", "net/", "os", "context"))]
|
|
129
|
+
lines: list[str] = []
|
|
130
|
+
if internal:
|
|
131
|
+
formatted = self._format_module_list([self._module_label(module) for module in internal])
|
|
132
|
+
if formatted:
|
|
133
|
+
lines.append(f"Orquesta paquetes internos: {formatted}.")
|
|
134
|
+
lines.append("Produce la salida principal del binario Go detectado.")
|
|
135
|
+
return lines
|
|
136
|
+
|
|
137
|
+
def _describe_entry_point(self, entry_point: EntryPoint, project_type: str | None) -> str:
|
|
138
|
+
if entry_point.kind == "cli" or entry_point.path.endswith("cli.py"):
|
|
139
|
+
return f"Entry point principal: {entry_point.path} expone la CLI del proyecto."
|
|
140
|
+
if entry_point.kind == "web":
|
|
141
|
+
return f"Entry point principal: {entry_point.path} arranca la interfaz web."
|
|
142
|
+
if project_type == "api" or entry_point.kind == "server":
|
|
143
|
+
return f"Entry point principal: {entry_point.path} arranca el servicio principal."
|
|
144
|
+
if entry_point.kind == "binary":
|
|
145
|
+
return f"Entry point principal: {entry_point.path} arranca el binario principal."
|
|
146
|
+
return f"Entry point principal: {entry_point.path} coordina el flujo principal del proyecto."
|
|
147
|
+
|
|
148
|
+
def _extract_optional_analyzer(self, node: ast.Assign) -> str | None:
|
|
149
|
+
value = node.value
|
|
150
|
+
if not isinstance(value, ast.IfExp):
|
|
151
|
+
return None
|
|
152
|
+
if not isinstance(value.test, ast.Name):
|
|
153
|
+
return None
|
|
154
|
+
if not isinstance(value.body, ast.Call):
|
|
155
|
+
return None
|
|
156
|
+
if not isinstance(value.body.func, ast.Name):
|
|
157
|
+
return None
|
|
158
|
+
analyzer_name = value.body.func.id
|
|
159
|
+
if not analyzer_name.endswith("Analyzer"):
|
|
160
|
+
return None
|
|
161
|
+
return f"{analyzer_name} (--{value.test.id.replace('_', '-')})"
|
|
162
|
+
|
|
163
|
+
def _infer_fallback_entry_points(
|
|
164
|
+
self, file_paths: list[str], stacks: list[StackDetection]
|
|
165
|
+
) -> list[EntryPoint]:
|
|
166
|
+
candidates: list[EntryPoint] = []
|
|
167
|
+
stack_name = stacks[0].stack if stacks else "unknown"
|
|
168
|
+
ordered_paths = sorted(file_paths, key=self._fallback_priority)
|
|
169
|
+
for path in ordered_paths:
|
|
170
|
+
if path.endswith(("cli.py", "__main__.py", "main.py")):
|
|
171
|
+
candidates.append(
|
|
172
|
+
EntryPoint(
|
|
173
|
+
path=path,
|
|
174
|
+
stack=stack_name,
|
|
175
|
+
kind="cli",
|
|
176
|
+
source="convention",
|
|
177
|
+
confidence="medium",
|
|
178
|
+
)
|
|
179
|
+
)
|
|
180
|
+
elif path.endswith(("app/page.tsx", "pages/index.js", "server.js")):
|
|
181
|
+
candidates.append(
|
|
182
|
+
EntryPoint(
|
|
183
|
+
path=path,
|
|
184
|
+
stack=stack_name,
|
|
185
|
+
kind="web" if "page" in path or "pages/" in path else "server",
|
|
186
|
+
source="convention",
|
|
187
|
+
confidence="medium",
|
|
188
|
+
)
|
|
189
|
+
)
|
|
190
|
+
elif path.endswith("main.go"):
|
|
191
|
+
candidates.append(
|
|
192
|
+
EntryPoint(
|
|
193
|
+
path=path,
|
|
194
|
+
stack=stack_name,
|
|
195
|
+
kind="binary",
|
|
196
|
+
source="convention",
|
|
197
|
+
confidence="medium",
|
|
198
|
+
)
|
|
199
|
+
)
|
|
200
|
+
return candidates
|
|
201
|
+
|
|
202
|
+
def _fallback_priority(self, path: str) -> tuple[int, int, str]:
|
|
203
|
+
return (
|
|
204
|
+
0 if "/cli.py" in path or path.endswith("cli.py") else 1,
|
|
205
|
+
0 if path.startswith("src/") else 1,
|
|
206
|
+
path,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
def _format_module_list(self, modules: list[str]) -> str:
|
|
210
|
+
normalized = [self._module_label(module) for module in modules]
|
|
211
|
+
filtered = [module for module in normalized if module and not self._is_tooling_path(module)]
|
|
212
|
+
if not filtered:
|
|
213
|
+
return ""
|
|
214
|
+
ordered: list[str] = []
|
|
215
|
+
seen: set[str] = set()
|
|
216
|
+
for module in filtered:
|
|
217
|
+
if module not in seen:
|
|
218
|
+
seen.add(module)
|
|
219
|
+
ordered.append(module)
|
|
220
|
+
return ", ".join(ordered[:8])
|
|
221
|
+
|
|
222
|
+
def _module_label(self, module: str) -> str:
|
|
223
|
+
cleaned = module.strip().strip("./")
|
|
224
|
+
if "/" in cleaned:
|
|
225
|
+
return cleaned.split("/")[-1]
|
|
226
|
+
if "." in cleaned:
|
|
227
|
+
return cleaned.rsplit(".", 1)[-1]
|
|
228
|
+
return cleaned
|
|
229
|
+
|
|
230
|
+
def _python_package_prefix(self, path: str) -> str:
|
|
231
|
+
parts = Path(path).parts
|
|
232
|
+
if len(parts) >= 3 and parts[0] == "src":
|
|
233
|
+
return f"{parts[1]}."
|
|
234
|
+
if len(parts) >= 2:
|
|
235
|
+
return f"{parts[0]}."
|
|
236
|
+
return ""
|
|
237
|
+
|
|
238
|
+
def _read_file(self, relative_path: str) -> str | None:
|
|
239
|
+
try:
|
|
240
|
+
return (self.root / relative_path).read_text(encoding="utf-8", errors="replace")
|
|
241
|
+
except OSError:
|
|
242
|
+
return None
|
|
243
|
+
|
|
244
|
+
def _is_tooling_path(self, path: str | None) -> bool:
|
|
245
|
+
if not path:
|
|
246
|
+
return False
|
|
247
|
+
normalized = path.strip().lstrip("/")
|
|
248
|
+
return normalized.startswith(_TOOLING_PREFIXES)
|
|
@@ -473,9 +473,10 @@ def main(
|
|
|
473
473
|
semantic_summary=sem_sum,
|
|
474
474
|
)
|
|
475
475
|
|
|
476
|
-
# Phase 9: LLM Output Quality — poblar campos derivados
|
|
477
|
-
from sourcecode.
|
|
478
|
-
from sourcecode.
|
|
476
|
+
# Phase 9: LLM Output Quality — poblar campos derivados
|
|
477
|
+
from sourcecode.architecture_summary import ArchitectureSummarizer
|
|
478
|
+
from sourcecode.summarizer import ProjectSummarizer
|
|
479
|
+
from sourcecode.tree_utils import flatten_file_tree
|
|
479
480
|
|
|
480
481
|
# LQN-01: lista plana de paths del file_tree con separador forward-slash
|
|
481
482
|
sm.file_paths = [
|
|
@@ -495,8 +496,9 @@ def main(
|
|
|
495
496
|
|
|
496
497
|
sm.key_dependencies = sorted(direct_deps, key=_dep_sort_key)[:15]
|
|
497
498
|
|
|
498
|
-
# LQN-02: resumen NL deterministico
|
|
499
|
-
sm.project_summary = ProjectSummarizer().generate(sm)
|
|
499
|
+
# LQN-02: resumen NL deterministico
|
|
500
|
+
sm.project_summary = ProjectSummarizer(target).generate(sm)
|
|
501
|
+
sm.architecture_summary = ArchitectureSummarizer(target).generate(sm)
|
|
500
502
|
|
|
501
503
|
# 4. Serializar (con o sin modo compact)
|
|
502
504
|
if compact:
|
|
@@ -39,7 +39,13 @@ class GoDetector(AbstractDetector):
|
|
|
39
39
|
]
|
|
40
40
|
preferred = [path for path in entry_candidates if path.startswith("cmd/")] or entry_candidates
|
|
41
41
|
entry_points = [
|
|
42
|
-
EntryPoint(
|
|
42
|
+
EntryPoint(
|
|
43
|
+
path=path,
|
|
44
|
+
stack="go",
|
|
45
|
+
kind="binary",
|
|
46
|
+
source="convention",
|
|
47
|
+
confidence="medium",
|
|
48
|
+
)
|
|
43
49
|
for path in unique_strings(preferred)
|
|
44
50
|
]
|
|
45
51
|
stack = StackDetection(
|
|
@@ -77,20 +77,33 @@ class NodejsDetector(AbstractDetector):
|
|
|
77
77
|
def _collect_entry_points(
|
|
78
78
|
self, context: DetectionContext, package_json: dict[str, Any]
|
|
79
79
|
) -> list[EntryPoint]:
|
|
80
|
-
|
|
80
|
+
entry_points: list[EntryPoint] = []
|
|
81
|
+
seen: set[str] = set()
|
|
81
82
|
main = package_json.get("main")
|
|
82
83
|
if isinstance(main, str) and main.strip():
|
|
83
|
-
|
|
84
|
+
path = main.strip()
|
|
85
|
+
if path_exists_in_tree(context.file_tree, path):
|
|
86
|
+
seen.add(path)
|
|
87
|
+
entry_points.append(
|
|
88
|
+
EntryPoint(
|
|
89
|
+
path=path,
|
|
90
|
+
stack="nodejs",
|
|
91
|
+
kind="server",
|
|
92
|
+
source="package.json",
|
|
93
|
+
confidence="high",
|
|
94
|
+
)
|
|
95
|
+
)
|
|
84
96
|
|
|
97
|
+
convention_candidates: list[str] = []
|
|
85
98
|
bin_field = package_json.get("bin")
|
|
86
99
|
if isinstance(bin_field, str) and bin_field.strip():
|
|
87
|
-
|
|
100
|
+
convention_candidates.append(bin_field.strip())
|
|
88
101
|
elif isinstance(bin_field, dict):
|
|
89
|
-
|
|
102
|
+
convention_candidates.extend(
|
|
90
103
|
str(value).strip() for value in bin_field.values() if isinstance(value, str) and value.strip()
|
|
91
104
|
)
|
|
92
105
|
|
|
93
|
-
|
|
106
|
+
convention_candidates.extend(
|
|
94
107
|
[
|
|
95
108
|
"server.js",
|
|
96
109
|
"src/index.js",
|
|
@@ -103,11 +116,17 @@ class NodejsDetector(AbstractDetector):
|
|
|
103
116
|
]
|
|
104
117
|
)
|
|
105
118
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
119
|
+
for path in unique_strings(convention_candidates):
|
|
120
|
+
if path in seen or not path_exists_in_tree(context.file_tree, path):
|
|
121
|
+
continue
|
|
122
|
+
kind = "web" if path.startswith(("app/", "pages/")) else "server"
|
|
123
|
+
entry_points.append(
|
|
124
|
+
EntryPoint(
|
|
125
|
+
path=path,
|
|
126
|
+
stack="nodejs",
|
|
127
|
+
kind=kind,
|
|
128
|
+
source="convention",
|
|
129
|
+
confidence="medium",
|
|
112
130
|
)
|
|
131
|
+
)
|
|
113
132
|
return entry_points
|
|
@@ -132,7 +132,7 @@ class PythonDetector(AbstractDetector):
|
|
|
132
132
|
return {match.lower() for match in matches}
|
|
133
133
|
|
|
134
134
|
def _collect_entry_points(self, context: DetectionContext) -> list[EntryPoint]:
|
|
135
|
-
|
|
135
|
+
declared_candidates: list[str] = []
|
|
136
136
|
pyproject = load_toml_file(context.root / "pyproject.toml")
|
|
137
137
|
if pyproject:
|
|
138
138
|
project = pyproject.get("project", {})
|
|
@@ -142,16 +142,38 @@ class PythonDetector(AbstractDetector):
|
|
|
142
142
|
for value in scripts.values():
|
|
143
143
|
if isinstance(value, str) and ":" in value:
|
|
144
144
|
module, _callable = value.split(":", 1)
|
|
145
|
-
|
|
145
|
+
declared_candidates.append(module.replace(".", "/") + ".py")
|
|
146
146
|
|
|
147
|
-
candidates.extend(["__main__.py", "main.py", "app.py", "manage.py", "src/main.py"])
|
|
148
147
|
entry_points: list[EntryPoint] = []
|
|
149
|
-
|
|
148
|
+
declared = set()
|
|
149
|
+
for path in unique_strings(declared_candidates):
|
|
150
150
|
if path_exists_in_tree(context.file_tree, path):
|
|
151
|
-
|
|
151
|
+
declared.add(path)
|
|
152
|
+
kind = "cli" if path.endswith(("__main__.py", "main.py", "cli.py")) else "app"
|
|
152
153
|
entry_points.append(
|
|
153
|
-
EntryPoint(
|
|
154
|
+
EntryPoint(
|
|
155
|
+
path=path,
|
|
156
|
+
stack="python",
|
|
157
|
+
kind=kind,
|
|
158
|
+
source="pyproject.toml",
|
|
159
|
+
confidence="high",
|
|
160
|
+
)
|
|
154
161
|
)
|
|
162
|
+
|
|
163
|
+
convention_candidates = ["cli.py", "__main__.py", "main.py", "app.py", "manage.py", "src/main.py"]
|
|
164
|
+
for path in unique_strings(convention_candidates):
|
|
165
|
+
if path in declared or not path_exists_in_tree(context.file_tree, path):
|
|
166
|
+
continue
|
|
167
|
+
kind = "cli" if path.endswith(("__main__.py", "main.py", "cli.py")) else "app"
|
|
168
|
+
entry_points.append(
|
|
169
|
+
EntryPoint(
|
|
170
|
+
path=path,
|
|
171
|
+
stack="python",
|
|
172
|
+
kind=kind,
|
|
173
|
+
source="convention",
|
|
174
|
+
confidence="medium",
|
|
175
|
+
)
|
|
176
|
+
)
|
|
155
177
|
return entry_points
|
|
156
178
|
|
|
157
179
|
def _detect_package_manager(
|
|
@@ -66,6 +66,7 @@ class EntryPoint:
|
|
|
66
66
|
stack: str
|
|
67
67
|
kind: str = "entry"
|
|
68
68
|
source: str = "manifest"
|
|
69
|
+
confidence: Literal["high", "medium", "low"] = "high"
|
|
69
70
|
|
|
70
71
|
|
|
71
72
|
@dataclass
|
|
@@ -322,6 +323,7 @@ class SourceMap:
|
|
|
322
323
|
# Phase 9: LLM Output Quality
|
|
323
324
|
file_paths: list[str] = field(default_factory=list)
|
|
324
325
|
project_summary: Optional[str] = None
|
|
326
|
+
architecture_summary: Optional[str] = None
|
|
325
327
|
key_dependencies: list[DependencyRecord] = field(default_factory=list)
|
|
326
328
|
# Phase 10: Code Quality Metrics
|
|
327
329
|
file_metrics: list[FileMetrics] = field(default_factory=list)
|