Add source preview to AI structure package
CI / python (push) Has been cancelled
CI / rust (push) Has been cancelled

This commit is contained in:
2026-05-22 15:43:24 +03:00
parent 7501111d29
commit 0a9ab94679
4 changed files with 46 additions and 0 deletions
@@ -62,9 +62,11 @@ def prepare_ai_structure(
diagnostics,
binaries,
_source_layout_summary(input_path),
_source_preview_summary(input_path),
)
_write_json(output_path / "manifest.json", manifest)
_write_json(output_path / "source_inventory.json", {"files": files})
_write_json(output_path / "source_preview.json", manifest.get("source_preview") or [])
if snapshot is not None:
(output_path / "sir_snapshot.json").write_bytes(snapshot_to_json(snapshot))
_write_json(output_path / "ai_objects.json", _ai_objects(snapshot))
@@ -104,6 +106,7 @@ def _manifest(
diagnostics: list[str],
binaries: list[dict[str, Any]],
source_layout: dict[str, Any],
source_preview: list[dict[str, Any]],
) -> dict[str, Any]:
return {
"version": AI_STRUCTURE_VERSION,
@@ -116,6 +119,7 @@ def _manifest(
"files_count": len(files),
"binary_1c_files": binaries,
"source_layout": source_layout,
"source_preview": source_preview,
"artifacts": _artifacts(snapshot, normalized),
"snapshot": None
if snapshot is None
@@ -148,6 +152,7 @@ def _artifacts(snapshot: SirSnapshot | None, normalized: NormalizedProject | Non
"export_plan.md",
"codex_package",
"project_layout.json",
"source_preview.json",
"compact_objects.json",
"compact_modules.json",
]
@@ -201,6 +206,7 @@ def _write_codex_package(
_write_json(root / "indexes" / "source-inventory.json", {"files": files})
_write_json(root / "indexes" / "source-map.json", {"files": source_map})
_write_json(root / "indexes" / "project-layout.json", manifest.get("source_layout") or {})
_write_json(root / "indexes" / "source-preview.json", manifest.get("source_preview") or [])
_write_json(root / "indexes" / "objects-compact.json", compact_objects)
_write_json(root / "indexes" / "modules-compact.json", compact_modules)
_write_json(root / "compact" / "objects.json", compact_objects)
@@ -592,6 +598,29 @@ def _source_layout_summary(root: Path) -> dict[str, Any]:
}
def _source_preview_summary(root: Path) -> list[dict[str, Any]]:
files = [root] if root.is_file() else sorted(path for path in root.rglob("*") if path.is_file())
object_files = _preview_relative_paths(
root,
sorted(
[path for path in files if path.suffix.casefold() in {".xml", ".mdo"}],
key=lambda path: (
0 if any(part.casefold() in {"configuration", "конфигурация"} for part in path.parts) else 1,
str(path).casefold(),
),
),
limit=6,
)
module_files = _preview_relative_paths(root, [path for path in files if path.suffix.casefold() == ".bsl"], limit=6)
layout = _source_layout_summary(root)
return [
{"label": "Главная конфигурация", "items": [layout["main_configuration_root"]]},
{"label": "Папки расширений", "items": layout["extension_roots"] or ["нет"]},
{"label": "Первые файлы объектов", "items": object_files or ["не найдены"]},
{"label": "Первые файлы модулей", "items": module_files or ["не найдены"]},
]
def _compact_objects(normalized: NormalizedProject | None) -> list[dict[str, Any]]:
if normalized is None:
return []
@@ -679,6 +708,16 @@ def _project_brief_markdown(manifest: dict[str, Any], compact_objects: list[dict
return "\n".join(lines) + "\n"
def _preview_relative_paths(root: Path, files: list[Path], *, limit: int) -> list[str]:
preview: list[str] = []
for path in files[:limit]:
if root.is_file():
preview.append(path.name)
else:
preview.append(path.relative_to(root).as_posix())
return preview
def _export_plan_markdown(project_id: str, input_path: Path, output_path: Path, binaries: list[dict[str, Any]], parseable: bool) -> str:
lines = [
f"# План выгрузки 1С для {project_id}",
@@ -210,6 +210,7 @@ def render_html5_ai_structure_result(result: dict | None) -> str:
snapshot = result.get("snapshot") or {}
normalized = result.get("normalized") or {}
source_layout = result.get("source_layout") or {}
source_preview = list(result.get("source_preview") or [])
status = _status_text(result.get("status"))
return f"""
<section class="ai-structure-result" data-html5-ai-structure-status="{escape(str(result.get('status', '')))}">
@@ -225,6 +226,7 @@ def render_html5_ai_structure_result(result: dict | None) -> str:
<div><dt>Объекты</dt><dd>{escape(str(normalized.get("objects", 0)))}</dd></div>
</dl>
{render_html5_ai_structure_result_summary(source_layout, normalized)}
{_render_ai_structure_preview_tree(source_preview)}
<div class="panel-title">Артефакты</div>
<div class="access-operations">{''.join(f'<article class="access-card"><strong>{escape(_artifact_text(item))}</strong><small>Файл пакета структуры</small></article>' for item in artifacts)}</div>
{_diagnostics(diagnostics)}
@@ -365,6 +367,7 @@ def _artifact_text(value: object) -> str:
"export_plan.md": "План выгрузки",
"codex_package": "Папка для Codex",
"project_layout.json": "Карта раскладки проекта",
"source_preview.json": "Краткий предпросмотр структуры",
"compact_objects.json": "Компактный индекс объектов",
"compact_modules.json": "Компактный индекс модулей",
"sir_snapshot.json": "Снимок графа SIR",
@@ -1409,6 +1409,7 @@ class AiStructurePrepareResponse(BaseModel):
files_count: int = 0
binary_1c_files: list[dict] = Field(default_factory=list)
source_layout: dict = Field(default_factory=dict)
source_preview: list[dict] = Field(default_factory=list)
artifacts: list[str] = Field(default_factory=list)
snapshot: dict | None = None
normalized: dict | None = None
+3
View File
@@ -1731,6 +1731,7 @@ def test_ai_structure_prepare_writes_ai_ready_package(tmp_path: Path):
assert (output / "normalized_project.json").exists()
assert (output / "sir_snapshot.json").exists()
assert (output / "project_layout.json").exists()
assert (output / "source_preview.json").exists()
assert (output / "compact_objects.json").exists()
assert (output / "compact_modules.json").exists()
assert (codex_package / "AGENTS.md").exists()
@@ -1740,6 +1741,7 @@ def test_ai_structure_prepare_writes_ai_ready_package(tmp_path: Path):
assert (codex_package / "context" / "project-overview.md").exists()
assert (codex_package / "indexes" / "codex-navigation.json").exists()
assert (codex_package / "indexes" / "project-layout.json").exists()
assert (codex_package / "indexes" / "source-preview.json").exists()
assert (codex_package / "indexes" / "objects-compact.json").exists()
assert (codex_package / "indexes" / "modules-compact.json").exists()
assert (codex_package / "indexes" / "objects.json").exists()
@@ -1842,6 +1844,7 @@ def test_ai_structure_prepare_understands_configuration_and_extension_folders(tm
assert payload["status"] == "ready"
assert payload["source_layout"]["main_configuration_root"] == "Конфигурация"
assert payload["source_layout"]["extension_roots"] == ["CRM"]
assert any(item["label"] == "Папки расширений" for item in payload["source_preview"])
assert payload["normalized"]["extensions"] == 1
codex_package = output / payload["codex_package_folder"]
layout = json.loads((codex_package / "indexes" / "project-layout.json").read_text(encoding="utf-8"))