deepdoc 1.9.3__tar.gz → 2.0.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.
- {deepdoc-1.9.3 → deepdoc-2.0.0}/PKG-INFO +1 -1
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/__init__.py +1 -1
- deepdoc-2.0.0/deepdoc/changelog_writer.py +106 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/cli.py +13 -13
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/generator/generation.py +1 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/generator/post_processors.py +36 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/persistence_v2.py +27 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/pipeline_v2.py +15 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/site/builder/scaffold_files.py +26 -1
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/smart_update_v2.py +104 -21
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc.egg-info/PKG-INFO +1 -1
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc.egg-info/SOURCES.txt +2 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/pyproject.toml +1 -1
- deepdoc-2.0.0/tests/test_changelog.py +74 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_classify.py +8 -12
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_cli_serve.py +2 -2
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_fumadocs_builder.py +1 -1
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_smart_update.py +104 -21
- {deepdoc-1.9.3 → deepdoc-2.0.0}/LICENSE +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/README.md +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/__main__.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/_legacy_types.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/benchmark_v2.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/call_graph.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/chatbot/__init__.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/chatbot/answer_mixin.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/chatbot/chunker.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/chatbot/deep_research.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/chatbot/docs_summary.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/chatbot/embeddings.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/chatbot/indexer.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/chatbot/linking.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/chatbot/live_fallback_mixin.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/chatbot/persistence.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/chatbot/providers.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/chatbot/retrieval_mixin.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/chatbot/routes.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/chatbot/scaffold.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/chatbot/service.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/chatbot/settings.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/chatbot/source_archive.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/chatbot/symbol_index.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/chatbot/types.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/config.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/generator/__init__.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/generator/evidence.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/generator/validation.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/llm/__init__.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/llm/client.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/llm/json_utils.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/llm/litellm_compat.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/manifest.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/openapi.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/parser/__init__.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/parser/api_detector.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/parser/base.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/parser/go_parser.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/parser/js_ts_parser.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/parser/php_parser.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/parser/python_parser.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/parser/registry.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/parser/routes/__init__.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/parser/routes/base.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/parser/routes/common.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/parser/routes/detector.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/parser/routes/django.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/parser/routes/express.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/parser/routes/falcon.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/parser/routes/fastify.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/parser/routes/go.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/parser/routes/js_shared.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/parser/routes/laravel.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/parser/routes/nestjs.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/parser/routes/python_shared.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/parser/routes/registry.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/parser/routes/repo_resolver.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/parser/vue_parser.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/planner/__init__.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/planner/bucket_injection.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/planner/bucket_refinement.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/planner/common.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/planner/endpoint_refs.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/planner/engine.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/planner/flow_candidates.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/planner/heuristics.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/planner/nav_shaping.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/planner/specializations.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/planner/topology.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/planner/utils.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/prompts/__init__.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/prompts/bucket_types.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/prompts/page_types.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/prompts/selectors.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/prompts/system.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/prompts/update.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/prompts_v2.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/py.typed +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/scanner/__init__.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/scanner/artifacts.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/scanner/clustering.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/scanner/common.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/scanner/database.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/scanner/endpoints.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/scanner/integrations.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/scanner/runtime.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/scanner/utils.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/site/__init__.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/site/builder/__init__.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/site/builder/chatbot_components.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/site/builder/common.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/site/builder/engine.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/site/builder/mdx_utils.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/site/builder/templates.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/source_metadata.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/updater_v2.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc/v2_models.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc.egg-info/dependency_links.txt +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc.egg-info/entry_points.txt +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc.egg-info/requires.txt +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/deepdoc.egg-info/top_level.txt +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/setup.cfg +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_benchmark_scorecard.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_call_graph.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_chatbot_config.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_chatbot_embeddings.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_chatbot_eval.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_chatbot_index.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_chatbot_persistence.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_chatbot_providers.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_chatbot_query.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_chatbot_relationship.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_chatbot_scaffold.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_chatbot_source_archive.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_cli_generate.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_cli_update.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_flow_candidates.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_framework_fixtures.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_framework_support.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_generation_evidence.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_internal_docs_metadata.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_litellm_compat.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_llm_json_utils.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_parallel_pipeline.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_parser_ranges.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_planner_consolidation.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_planner_granularity.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_route_registry.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_runtime_scan.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_stale.py +0 -0
- {deepdoc-1.9.3 → deepdoc-2.0.0}/tests/test_state.py +0 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from .persistence_v2 import append_changelog_entry, load_changelog, load_plan, save_plan
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def record_and_write(
|
|
9
|
+
repo_root: Path,
|
|
10
|
+
output_dir: Path,
|
|
11
|
+
*,
|
|
12
|
+
commit: str,
|
|
13
|
+
commit_message: str,
|
|
14
|
+
commit_date: str,
|
|
15
|
+
strategy: str,
|
|
16
|
+
pages_updated: list[str],
|
|
17
|
+
files_changed: list[str],
|
|
18
|
+
is_initial: bool = False,
|
|
19
|
+
) -> None:
|
|
20
|
+
"""Append one changelog entry and regenerate whats-changed.mdx."""
|
|
21
|
+
entry = {
|
|
22
|
+
"commit": commit[:8],
|
|
23
|
+
"date": commit_date,
|
|
24
|
+
"commit_message": commit_message,
|
|
25
|
+
"strategy": strategy,
|
|
26
|
+
"pages_updated": pages_updated,
|
|
27
|
+
"files_changed": files_changed[:20],
|
|
28
|
+
"is_initial": is_initial,
|
|
29
|
+
}
|
|
30
|
+
append_changelog_entry(repo_root, entry)
|
|
31
|
+
write_whats_changed_page(repo_root, output_dir)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def write_whats_changed_page(repo_root: Path, output_dir: Path) -> None:
|
|
35
|
+
"""Write docs/whats-changed.mdx from .deepdoc/changelog.json."""
|
|
36
|
+
entries = load_changelog(repo_root)
|
|
37
|
+
mdx = _build_mdx(entries)
|
|
38
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
39
|
+
(output_dir / "whats-changed.mdx").write_text(mdx, encoding="utf-8")
|
|
40
|
+
_ensure_in_nav(repo_root)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _build_mdx(entries: list[dict]) -> str:
|
|
44
|
+
lines = [
|
|
45
|
+
"---",
|
|
46
|
+
'title: "What\'s Changed"',
|
|
47
|
+
'description: "Documentation changes per commit"',
|
|
48
|
+
"---",
|
|
49
|
+
"",
|
|
50
|
+
"# What's Changed",
|
|
51
|
+
"",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
if not entries:
|
|
55
|
+
lines.append("No changes recorded yet — this is the initial documentation.")
|
|
56
|
+
return "\n".join(lines)
|
|
57
|
+
|
|
58
|
+
lines.append("<Accordions>")
|
|
59
|
+
for entry in entries:
|
|
60
|
+
date = entry.get("date", "")
|
|
61
|
+
msg = entry.get("commit_message", "update")[:80]
|
|
62
|
+
sha = entry.get("commit", "")
|
|
63
|
+
pages = entry.get("pages_updated", [])
|
|
64
|
+
files = entry.get("files_changed", [])
|
|
65
|
+
is_initial = entry.get("is_initial", False)
|
|
66
|
+
|
|
67
|
+
title = f"{date} — {msg} ({sha})"
|
|
68
|
+
lines.append(f'<Accordion title="{title}">')
|
|
69
|
+
lines.append("")
|
|
70
|
+
|
|
71
|
+
if is_initial:
|
|
72
|
+
lines.append(f"**{len(pages)} pages generated** — initial documentation run.")
|
|
73
|
+
else:
|
|
74
|
+
if pages:
|
|
75
|
+
page_links = ", ".join(f"[{_slug_to_title(s)}](/{s})" for s in pages)
|
|
76
|
+
lines.append(f"**{len(pages)} page(s) updated:** {page_links}")
|
|
77
|
+
else:
|
|
78
|
+
lines.append("No pages regenerated.")
|
|
79
|
+
|
|
80
|
+
if files and not is_initial:
|
|
81
|
+
file_list = ", ".join(f"`{f}`" for f in files[:10])
|
|
82
|
+
if len(files) > 10:
|
|
83
|
+
file_list += f" +{len(files) - 10} more"
|
|
84
|
+
lines.append("")
|
|
85
|
+
lines.append(f"**Files changed:** {file_list}")
|
|
86
|
+
|
|
87
|
+
lines.append("")
|
|
88
|
+
lines.append("</Accordion>")
|
|
89
|
+
|
|
90
|
+
lines.append("</Accordions>")
|
|
91
|
+
return "\n".join(lines)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _slug_to_title(slug: str) -> str:
|
|
95
|
+
return slug.replace("-", " ").title()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _ensure_in_nav(repo_root: Path) -> None:
|
|
99
|
+
"""Add whats-changed to Start Here section of the saved plan if not already there."""
|
|
100
|
+
plan = load_plan(repo_root)
|
|
101
|
+
if plan is None or not hasattr(plan, "nav_structure"):
|
|
102
|
+
return
|
|
103
|
+
section = plan.nav_structure.setdefault("Start Here", [])
|
|
104
|
+
if "whats-changed" not in section:
|
|
105
|
+
section.append("whats-changed")
|
|
106
|
+
save_plan(plan, repo_root)
|
|
@@ -1200,13 +1200,17 @@ def _warn_if_deprecated_generated_version(cfg: dict, repo_root: Path) -> None:
|
|
|
1200
1200
|
if not warning_cfg.get("enabled", True):
|
|
1201
1201
|
return
|
|
1202
1202
|
|
|
1203
|
-
minimum_version = str(warning_cfg.get("minimum_version") or "1.0.0")
|
|
1204
1203
|
generated_version = _detect_generated_deepdoc_version(
|
|
1205
1204
|
repo_root, repo_root / str(cfg.get("output_dir", "docs") or "docs")
|
|
1206
1205
|
)
|
|
1207
1206
|
if generated_version is None:
|
|
1208
1207
|
return
|
|
1209
|
-
|
|
1208
|
+
|
|
1209
|
+
# Warn when the docs were generated by a different major version of the CLI.
|
|
1210
|
+
# Minor/patch bumps are backwards-compatible — no need to nag.
|
|
1211
|
+
gen_major = _version_tuple(generated_version)[0]
|
|
1212
|
+
cli_major = _version_tuple(__version__)[0]
|
|
1213
|
+
if gen_major >= cli_major:
|
|
1210
1214
|
return
|
|
1211
1215
|
|
|
1212
1216
|
resolved_root = repo_root.resolve()
|
|
@@ -1214,19 +1218,15 @@ def _warn_if_deprecated_generated_version(cfg: dict, repo_root: Path) -> None:
|
|
|
1214
1218
|
return
|
|
1215
1219
|
_DEPRECATED_VERSION_WARNING_REPOS.add(resolved_root)
|
|
1216
1220
|
|
|
1217
|
-
upgrade_command = str(
|
|
1218
|
-
warning_cfg.get("upgrade_command")
|
|
1219
|
-
or "python3 -m pip install --upgrade deepdoc"
|
|
1220
|
-
)
|
|
1221
1221
|
console.print(
|
|
1222
1222
|
Panel.fit(
|
|
1223
|
-
"[bold yellow]
|
|
1224
|
-
f"
|
|
1225
|
-
f"
|
|
1226
|
-
"
|
|
1227
|
-
|
|
1228
|
-
"To
|
|
1229
|
-
"`compatibility.deprecated_version_warning
|
|
1223
|
+
"[bold yellow]Docs need regeneration[/bold yellow]\n\n"
|
|
1224
|
+
f"Your docs were generated with DeepDoc [bold]{generated_version}[/bold] "
|
|
1225
|
+
f"but the current CLI is [bold]{__version__}[/bold] (different major version).\n"
|
|
1226
|
+
"Run the following to bring them up to date:\n\n"
|
|
1227
|
+
"[bold]deepdoc generate[/bold]\n\n"
|
|
1228
|
+
"To disable this warning: set "
|
|
1229
|
+
"`compatibility.deprecated_version_warning.enabled: false` in `.deepdoc.yaml`.",
|
|
1230
1230
|
border_style="yellow",
|
|
1231
1231
|
)
|
|
1232
1232
|
)
|
|
@@ -1072,6 +1072,7 @@ Re-run `deepdoc generate` to retry.
|
|
|
1072
1072
|
"deepdoc_generated_version": DEEPDOC_VERSION,
|
|
1073
1073
|
"deepdoc_status": status_value,
|
|
1074
1074
|
"deepdoc_evidence_files": evidence_files[:50],
|
|
1075
|
+
"deepdoc_prereqs": bucket.depends_on,
|
|
1075
1076
|
}
|
|
1076
1077
|
return _merge_frontmatter_fields(content, fields)
|
|
1077
1078
|
|
|
@@ -957,6 +957,10 @@ def repair_mdx_component_blocks(content: str) -> str:
|
|
|
957
957
|
|
|
958
958
|
content = pattern.sub(_rewrite, content)
|
|
959
959
|
|
|
960
|
+
# Fix Accordion nesting first (inserts missing </Accordion> before </Accordions>)
|
|
961
|
+
# so the subsequent count-based repair sees balanced tags and doesn't double-add.
|
|
962
|
+
content = _repair_accordion_nesting(content)
|
|
963
|
+
|
|
960
964
|
for component in multiline_components:
|
|
961
965
|
open_count = len(re.findall(rf"<{component}(?:\s[^>]*)?>", content))
|
|
962
966
|
close_count = len(re.findall(rf"</{component}>", content))
|
|
@@ -968,6 +972,38 @@ def repair_mdx_component_blocks(content: str) -> str:
|
|
|
968
972
|
return content
|
|
969
973
|
|
|
970
974
|
|
|
975
|
+
def _repair_accordion_nesting(content: str) -> str:
|
|
976
|
+
"""Fix Accordion nesting issues emitted by the LLM.
|
|
977
|
+
|
|
978
|
+
Two cases handled:
|
|
979
|
+
1. Missing </Accordion> before </Accordions> — inserts them in the right place.
|
|
980
|
+
2. Swapped order (</Accordions> then </Accordion>) — strips the orphaned
|
|
981
|
+
</Accordion> that appears after </Accordions> with no matching open tag.
|
|
982
|
+
"""
|
|
983
|
+
import re as _re
|
|
984
|
+
|
|
985
|
+
def _fix_block(m: re.Match) -> str:
|
|
986
|
+
block = m.group(0)
|
|
987
|
+
opens = len(_re.findall(r"<Accordion(?:\s[^>]*)?>", block))
|
|
988
|
+
closes = len(_re.findall(r"</Accordion>", block))
|
|
989
|
+
deficit = opens - closes
|
|
990
|
+
if deficit <= 0:
|
|
991
|
+
return block
|
|
992
|
+
return block[:-len("</Accordions>")] + ("</Accordion>\n" * deficit) + "</Accordions>"
|
|
993
|
+
|
|
994
|
+
content = re.sub(
|
|
995
|
+
r"<Accordions(?:\s[^>]*)?>[\s\S]*?</Accordions>",
|
|
996
|
+
_fix_block,
|
|
997
|
+
content,
|
|
998
|
+
)
|
|
999
|
+
|
|
1000
|
+
# Strip orphaned </Accordion> tags that appear after </Accordions>
|
|
1001
|
+
# (the LLM sometimes emits </Accordions>\n</Accordion> in the wrong order)
|
|
1002
|
+
content = re.sub(r"(</Accordions>)(\s*</Accordion>)+", r"\1", content)
|
|
1003
|
+
|
|
1004
|
+
return content
|
|
1005
|
+
|
|
1006
|
+
|
|
971
1007
|
_PROVENANCE_KEYS = frozenset({
|
|
972
1008
|
"deepdoc_generated_at",
|
|
973
1009
|
"deepdoc_generated_commit",
|
|
@@ -37,6 +37,8 @@ LEDGER_FILE = "ledger.json"
|
|
|
37
37
|
FILE_MAP_FILE = "file_map.json"
|
|
38
38
|
STATE_FILE = "state.json"
|
|
39
39
|
SYNC_RECEIPT_FILE = "sync_receipt.json"
|
|
40
|
+
CHANGELOG_FILE = "changelog.json"
|
|
41
|
+
CHANGELOG_MAX_ENTRIES = 50
|
|
40
42
|
ENGINE_FINGERPRINT = "routes_repo_resolution_v2_trimmed_scope"
|
|
41
43
|
|
|
42
44
|
# Legacy top-level files (kept for backwards-compat)
|
|
@@ -108,6 +110,31 @@ def load_sync_state(repo_root: Path) -> dict[str, Any] | None:
|
|
|
108
110
|
return None
|
|
109
111
|
|
|
110
112
|
|
|
113
|
+
def append_changelog_entry(repo_root: Path, entry: dict[str, Any]) -> None:
|
|
114
|
+
"""Append one entry to .deepdoc/changelog.json, capped at CHANGELOG_MAX_ENTRIES."""
|
|
115
|
+
path = _state_dir(repo_root) / CHANGELOG_FILE
|
|
116
|
+
entries: list[dict[str, Any]] = []
|
|
117
|
+
if path.exists():
|
|
118
|
+
try:
|
|
119
|
+
entries = json.loads(path.read_text(encoding="utf-8"))
|
|
120
|
+
except Exception:
|
|
121
|
+
entries = []
|
|
122
|
+
entries.insert(0, entry)
|
|
123
|
+
entries = entries[:CHANGELOG_MAX_ENTRIES]
|
|
124
|
+
path.write_text(json.dumps(entries, indent=2), encoding="utf-8")
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def load_changelog(repo_root: Path) -> list[dict[str, Any]]:
|
|
128
|
+
"""Read .deepdoc/changelog.json. Returns empty list if not present or corrupt."""
|
|
129
|
+
path = _state_dir(repo_root) / CHANGELOG_FILE
|
|
130
|
+
if not path.exists():
|
|
131
|
+
return []
|
|
132
|
+
try:
|
|
133
|
+
return json.loads(path.read_text(encoding="utf-8"))
|
|
134
|
+
except Exception:
|
|
135
|
+
return []
|
|
136
|
+
|
|
137
|
+
|
|
111
138
|
def save_sync_receipt(repo_root: Path, receipt: dict[str, Any]) -> None:
|
|
112
139
|
"""Write a top-level receipt for the latest generate/update sync run."""
|
|
113
140
|
path = _state_dir(repo_root) / SYNC_RECEIPT_FILE
|
|
@@ -51,8 +51,10 @@ from .openapi import (
|
|
|
51
51
|
parse_openapi_spec,
|
|
52
52
|
spec_to_context_string,
|
|
53
53
|
)
|
|
54
|
+
from .changelog_writer import record_and_write as _record_changelog
|
|
54
55
|
from .persistence_v2 import (
|
|
55
56
|
cleanup_stale_generated_files,
|
|
57
|
+
load_changelog,
|
|
56
58
|
load_generation_ledger,
|
|
57
59
|
prune_generation_ledger,
|
|
58
60
|
save_all,
|
|
@@ -424,6 +426,19 @@ class PipelineV2:
|
|
|
424
426
|
"replanned": True,
|
|
425
427
|
},
|
|
426
428
|
)
|
|
429
|
+
changelog_exists = bool(load_changelog(self.repo_root))
|
|
430
|
+
_commit_obj = _repo.head.commit
|
|
431
|
+
_record_changelog(
|
|
432
|
+
self.repo_root,
|
|
433
|
+
self.output_dir,
|
|
434
|
+
commit=head_sha,
|
|
435
|
+
commit_message=_commit_obj.message.strip().splitlines()[0],
|
|
436
|
+
commit_date=_commit_obj.committed_datetime.strftime("%Y-%m-%d"),
|
|
437
|
+
strategy="full_generate",
|
|
438
|
+
pages_updated=[b.slug for b in plan.buckets],
|
|
439
|
+
files_changed=[],
|
|
440
|
+
is_initial=not changelog_exists,
|
|
441
|
+
)
|
|
427
442
|
except Exception:
|
|
428
443
|
pass # Not a git repo or detached HEAD — skip silently
|
|
429
444
|
|
|
@@ -1421,7 +1421,9 @@ def _docs_page_tsx() -> str:
|
|
|
1421
1421
|
"""\
|
|
1422
1422
|
import { notFound } from 'next/navigation';
|
|
1423
1423
|
import { DocsBody, DocsPage } from 'fumadocs-ui/page';
|
|
1424
|
+
import { findNeighbour } from 'fumadocs-core/server';
|
|
1424
1425
|
import { docsSource } from '@/lib/source';
|
|
1426
|
+
import { pageTree } from '@/lib/page-tree.generated';
|
|
1425
1427
|
import { getMDXComponents } from '@/mdx-components';
|
|
1426
1428
|
import type { ComponentType } from 'react';
|
|
1427
1429
|
import type { TOCItemType } from 'fumadocs-core/server';
|
|
@@ -1442,6 +1444,7 @@ def _docs_page_tsx() -> str:
|
|
|
1442
1444
|
const meta = page.data as {
|
|
1443
1445
|
deepdoc_generated_at?: string;
|
|
1444
1446
|
deepdoc_generated_commit?: string;
|
|
1447
|
+
deepdoc_prereqs?: string[];
|
|
1445
1448
|
};
|
|
1446
1449
|
const lastIndexed = meta.deepdoc_generated_at
|
|
1447
1450
|
? new Intl.DateTimeFormat('en-GB', {
|
|
@@ -1459,10 +1462,32 @@ def _docs_page_tsx() -> str:
|
|
|
1459
1462
|
: commitId
|
|
1460
1463
|
? `Last indexed: (${commitId})`
|
|
1461
1464
|
: null;
|
|
1465
|
+
const prereqs = meta.deepdoc_prereqs ?? [];
|
|
1466
|
+
const { previous, next } = findNeighbour(pageTree, page.url);
|
|
1462
1467
|
|
|
1463
1468
|
return (
|
|
1464
|
-
<DocsPage toc={toc}>
|
|
1469
|
+
<DocsPage toc={toc} prev={previous ?? false} next={next ?? false}>
|
|
1465
1470
|
<DocsBody>
|
|
1471
|
+
{prereqs.length > 0 ? (
|
|
1472
|
+
<p
|
|
1473
|
+
style={{
|
|
1474
|
+
marginTop: 0,
|
|
1475
|
+
marginBottom: '0.5rem',
|
|
1476
|
+
fontSize: '0.875rem',
|
|
1477
|
+
color: 'var(--color-fd-muted-foreground)',
|
|
1478
|
+
}}
|
|
1479
|
+
>
|
|
1480
|
+
{'Read first: '}
|
|
1481
|
+
{prereqs.map((slug: string, i: number) => (
|
|
1482
|
+
<span key={slug}>
|
|
1483
|
+
<a href={`/${slug}`} style={{ textDecoration: 'underline' }}>
|
|
1484
|
+
{slug.replace(/-/g, ' ').replace(/\\b\\w/g, (c: string) => c.toUpperCase())}
|
|
1485
|
+
</a>
|
|
1486
|
+
{i < prereqs.length - 1 ? ', ' : ''}
|
|
1487
|
+
</span>
|
|
1488
|
+
))}
|
|
1489
|
+
</p>
|
|
1490
|
+
) : null}
|
|
1466
1491
|
{lastIndexedLabel ? (
|
|
1467
1492
|
<p
|
|
1468
1493
|
style={{
|
|
@@ -35,14 +35,17 @@ from .generator import summarize_generation_results
|
|
|
35
35
|
from .llm import LLMClient
|
|
36
36
|
from .manifest import Manifest
|
|
37
37
|
from .parser import supported_extensions
|
|
38
|
+
from .changelog_writer import record_and_write as _record_changelog
|
|
38
39
|
from .persistence_v2 import (
|
|
39
40
|
ENGINE_FINGERPRINT,
|
|
41
|
+
cleanup_stale_generated_files,
|
|
40
42
|
find_stale_buckets,
|
|
41
43
|
ledger_summary,
|
|
42
44
|
load_generation_ledger,
|
|
43
45
|
load_plan,
|
|
44
46
|
load_scan_cache,
|
|
45
47
|
load_sync_state,
|
|
48
|
+
prune_generation_ledger,
|
|
46
49
|
save_all,
|
|
47
50
|
save_sync_receipt,
|
|
48
51
|
save_sync_state,
|
|
@@ -51,10 +54,6 @@ from .v2_models import DocPlan, endpoint_owned_files, tracked_bucket_files
|
|
|
51
54
|
|
|
52
55
|
console = Console()
|
|
53
56
|
|
|
54
|
-
# What fraction of total files changed triggers a replan
|
|
55
|
-
REPLAN_THRESHOLD = 0.20
|
|
56
|
-
# New files added beyond this count also triggers replan
|
|
57
|
-
NEW_FILES_REPLAN_THRESHOLD = 5
|
|
58
57
|
|
|
59
58
|
|
|
60
59
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -98,19 +97,13 @@ class ChangeSet:
|
|
|
98
97
|
and not self.orphaned_bucket_slugs
|
|
99
98
|
):
|
|
100
99
|
return "noop"
|
|
101
|
-
if self.deleted_files or self.orphaned_bucket_slugs:
|
|
102
|
-
return "full_replan"
|
|
103
|
-
if len(self.new_files) >= NEW_FILES_REPLAN_THRESHOLD:
|
|
104
|
-
return "full_replan"
|
|
105
|
-
# If total changes exceed the percentage threshold, replan
|
|
106
100
|
if (
|
|
107
|
-
self.
|
|
108
|
-
|
|
101
|
+
self.new_files
|
|
102
|
+
or self.new_integration_signals
|
|
103
|
+
or self.deleted_files
|
|
104
|
+
or self.orphaned_bucket_slugs
|
|
105
|
+
or self.endpoint_structure_changed
|
|
109
106
|
):
|
|
110
|
-
return "full_replan"
|
|
111
|
-
if self.endpoint_structure_changed:
|
|
112
|
-
return "full_replan"
|
|
113
|
-
if self.new_files or self.new_integration_signals:
|
|
114
107
|
return "targeted_replan"
|
|
115
108
|
return "incremental"
|
|
116
109
|
|
|
@@ -257,11 +250,12 @@ class SmartUpdater:
|
|
|
257
250
|
return stats
|
|
258
251
|
|
|
259
252
|
elif sync_plan.strategy == "full_replan":
|
|
253
|
+
# Only reachable via engine_mismatch or explicit force_replan override
|
|
254
|
+
# in _build_sync_plan — never auto-triggered by ChangeSet.strategy.
|
|
260
255
|
console.print(
|
|
261
256
|
Panel(
|
|
262
257
|
"[bold yellow]Strategy: Full Replan[/bold yellow]\n"
|
|
263
|
-
|
|
264
|
-
f"{len(change_set.deleted_files)} deleted files",
|
|
258
|
+
"Reason: engine fingerprint changed or replan explicitly forced",
|
|
265
259
|
border_style="yellow",
|
|
266
260
|
)
|
|
267
261
|
)
|
|
@@ -276,6 +270,12 @@ class SmartUpdater:
|
|
|
276
270
|
f"{len(change_set.new_integration_signals)} new integration signal(s): "
|
|
277
271
|
f"{', '.join(change_set.new_integration_signals[:5])}"
|
|
278
272
|
)
|
|
273
|
+
if change_set.deleted_files:
|
|
274
|
+
reasons.append(f"{len(change_set.deleted_files)} deleted file(s)")
|
|
275
|
+
if change_set.orphaned_bucket_slugs:
|
|
276
|
+
reasons.append(f"{len(change_set.orphaned_bucket_slugs)} orphaned bucket(s)")
|
|
277
|
+
if change_set.endpoint_structure_changed:
|
|
278
|
+
reasons.append("endpoint structure changed")
|
|
279
279
|
console.print(
|
|
280
280
|
Panel(
|
|
281
281
|
"[bold cyan]Strategy: Targeted Replan[/bold cyan]\n"
|
|
@@ -308,12 +308,13 @@ class SmartUpdater:
|
|
|
308
308
|
stats["chatbot_failed"] = run_result.chatbot_failed
|
|
309
309
|
|
|
310
310
|
# ── Step 4: Rebuild site nav ───────────────────────────────────
|
|
311
|
-
if executed_strategy
|
|
311
|
+
if executed_strategy != "noop":
|
|
312
312
|
updated_plan = load_plan(self.repo_root) or plan
|
|
313
313
|
self._rebuild_nav(updated_plan)
|
|
314
314
|
|
|
315
315
|
# ── Step 5: Persist sync baseline ──────────────────────────────
|
|
316
|
-
# full_replan
|
|
316
|
+
# When full_replan fires (engine_mismatch / force_replan), pipeline_v2
|
|
317
|
+
# already saves state internally — skip double-save only for that case.
|
|
317
318
|
if executed_strategy != "full_replan":
|
|
318
319
|
self._save_update_sync_state(
|
|
319
320
|
target_commit=sync_plan.target_commit,
|
|
@@ -323,6 +324,8 @@ class SmartUpdater:
|
|
|
323
324
|
plan=plan,
|
|
324
325
|
)
|
|
325
326
|
self._save_update_sync_receipt(sync_plan, run_result)
|
|
327
|
+
if executed_strategy != "noop":
|
|
328
|
+
self._append_changelog(sync_plan, run_result)
|
|
326
329
|
|
|
327
330
|
console.print(
|
|
328
331
|
Panel.fit(
|
|
@@ -390,6 +393,54 @@ class SmartUpdater:
|
|
|
390
393
|
chatbot_failed=bool(result.get("chatbot_error")),
|
|
391
394
|
)
|
|
392
395
|
|
|
396
|
+
def _handle_deleted_files(
|
|
397
|
+
self, plan: DocPlan, change_set: ChangeSet
|
|
398
|
+
) -> DocPlan:
|
|
399
|
+
"""Remove deleted files from bucket owned_files; clean up fully empty buckets."""
|
|
400
|
+
if not change_set.deleted_files and not change_set.orphaned_bucket_slugs:
|
|
401
|
+
return plan
|
|
402
|
+
|
|
403
|
+
deleted_set = set(change_set.deleted_files)
|
|
404
|
+
orphaned_set = set(change_set.orphaned_bucket_slugs)
|
|
405
|
+
updated_buckets = []
|
|
406
|
+
removed_slugs: list[str] = []
|
|
407
|
+
|
|
408
|
+
for bucket in plan.buckets:
|
|
409
|
+
if bucket.slug in orphaned_set:
|
|
410
|
+
removed_slugs.append(bucket.slug)
|
|
411
|
+
continue
|
|
412
|
+
if any(f in deleted_set for f in bucket.owned_files):
|
|
413
|
+
bucket.owned_files = [
|
|
414
|
+
f for f in bucket.owned_files if f not in deleted_set
|
|
415
|
+
]
|
|
416
|
+
if bucket.slug not in change_set.stale_bucket_slugs:
|
|
417
|
+
change_set.stale_bucket_slugs.append(bucket.slug)
|
|
418
|
+
updated_buckets.append(bucket)
|
|
419
|
+
|
|
420
|
+
if removed_slugs:
|
|
421
|
+
keep_slugs = {b.slug for b in updated_buckets}
|
|
422
|
+
deleted_paths = cleanup_stale_generated_files(
|
|
423
|
+
self.repo_root, self.output_dir, keep_slugs
|
|
424
|
+
)
|
|
425
|
+
prune_generation_ledger(self.repo_root, keep_slugs)
|
|
426
|
+
if deleted_paths:
|
|
427
|
+
console.print(
|
|
428
|
+
f" [dim]Removed {len(deleted_paths)} doc(s) for deleted bucket(s): "
|
|
429
|
+
f"{', '.join(removed_slugs)}[/dim]"
|
|
430
|
+
)
|
|
431
|
+
plan.nav_structure = {
|
|
432
|
+
section: [s for s in slugs if s not in removed_slugs]
|
|
433
|
+
for section, slugs in plan.nav_structure.items()
|
|
434
|
+
}
|
|
435
|
+
plan.nav_structure = {
|
|
436
|
+
section: slugs
|
|
437
|
+
for section, slugs in plan.nav_structure.items()
|
|
438
|
+
if slugs
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
plan.buckets = updated_buckets
|
|
442
|
+
return plan
|
|
443
|
+
|
|
393
444
|
def _targeted_replan(self, plan: DocPlan, change_set: ChangeSet) -> UpdateRunResult:
|
|
394
445
|
"""Replan only to discover new buckets for new integrations/files,
|
|
395
446
|
then merge with existing plan and regenerate stale buckets.
|
|
@@ -403,6 +454,9 @@ class SmartUpdater:
|
|
|
403
454
|
scan_repo as bucket_scan_repo,
|
|
404
455
|
)
|
|
405
456
|
|
|
457
|
+
# Step 0: clean up deleted files and orphaned buckets before any LLM work
|
|
458
|
+
plan = self._handle_deleted_files(plan, change_set)
|
|
459
|
+
|
|
406
460
|
# Invalidate call graph cache if source files changed
|
|
407
461
|
_invalidate_call_graph_cache(
|
|
408
462
|
self.repo_root,
|
|
@@ -434,9 +488,8 @@ class SmartUpdater:
|
|
|
434
488
|
|
|
435
489
|
if change_set.new_files and not added:
|
|
436
490
|
console.print(
|
|
437
|
-
"[yellow]⚠ New files did not map
|
|
491
|
+
"[yellow]⚠ New files did not map to new buckets — they will be picked up on the next explicit replan[/yellow]"
|
|
438
492
|
)
|
|
439
|
-
return self._full_replan_and_generate()
|
|
440
493
|
|
|
441
494
|
if added:
|
|
442
495
|
console.print(f" [green]+{len(added)} new bucket(s) discovered:[/green]")
|
|
@@ -1128,6 +1181,36 @@ class SmartUpdater:
|
|
|
1128
1181
|
},
|
|
1129
1182
|
)
|
|
1130
1183
|
|
|
1184
|
+
def _append_changelog(
|
|
1185
|
+
self,
|
|
1186
|
+
sync_plan: UpdateSyncPlan,
|
|
1187
|
+
run_result: UpdateRunResult,
|
|
1188
|
+
) -> None:
|
|
1189
|
+
"""Append a changelog entry and regenerate whats-changed.mdx."""
|
|
1190
|
+
try:
|
|
1191
|
+
import git as _git
|
|
1192
|
+
|
|
1193
|
+
repo = _git.Repo(self.repo_root)
|
|
1194
|
+
commit_obj = repo.commit(sync_plan.target_commit)
|
|
1195
|
+
commit_message = commit_obj.message.strip().splitlines()[0]
|
|
1196
|
+
commit_date = commit_obj.committed_datetime.strftime("%Y-%m-%d")
|
|
1197
|
+
except Exception:
|
|
1198
|
+
commit_message = "update"
|
|
1199
|
+
commit_date = ""
|
|
1200
|
+
|
|
1201
|
+
_record_changelog(
|
|
1202
|
+
self.repo_root,
|
|
1203
|
+
self.output_dir,
|
|
1204
|
+
commit=sync_plan.target_commit,
|
|
1205
|
+
commit_message=commit_message,
|
|
1206
|
+
commit_date=commit_date,
|
|
1207
|
+
strategy=run_result.strategy,
|
|
1208
|
+
pages_updated=list(run_result.updated_slugs),
|
|
1209
|
+
files_changed=list(
|
|
1210
|
+
sync_plan.change_set.changed_files + sync_plan.change_set.new_files
|
|
1211
|
+
),
|
|
1212
|
+
)
|
|
1213
|
+
|
|
1131
1214
|
def _resolve_head_commit(self) -> str:
|
|
1132
1215
|
"""Return the current HEAD commit SHA."""
|
|
1133
1216
|
import git as _git
|
|
@@ -6,6 +6,7 @@ deepdoc/__main__.py
|
|
|
6
6
|
deepdoc/_legacy_types.py
|
|
7
7
|
deepdoc/benchmark_v2.py
|
|
8
8
|
deepdoc/call_graph.py
|
|
9
|
+
deepdoc/changelog_writer.py
|
|
9
10
|
deepdoc/cli.py
|
|
10
11
|
deepdoc/config.py
|
|
11
12
|
deepdoc/manifest.py
|
|
@@ -113,6 +114,7 @@ deepdoc/site/builder/scaffold_files.py
|
|
|
113
114
|
deepdoc/site/builder/templates.py
|
|
114
115
|
tests/test_benchmark_scorecard.py
|
|
115
116
|
tests/test_call_graph.py
|
|
117
|
+
tests/test_changelog.py
|
|
116
118
|
tests/test_chatbot_config.py
|
|
117
119
|
tests/test_chatbot_embeddings.py
|
|
118
120
|
tests/test_chatbot_eval.py
|