deepdoc 2.2.0__tar.gz → 2.3.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.
Files changed (157) hide show
  1. {deepdoc-2.2.0 → deepdoc-2.3.0}/PKG-INFO +13 -1
  2. {deepdoc-2.2.0 → deepdoc-2.3.0}/README.md +12 -0
  3. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/__init__.py +1 -1
  4. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/changelog_writer.py +11 -11
  5. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/chatbot/answer_mixin.py +6 -12
  6. deepdoc-2.3.0/deepdoc/chatbot/constants.py +27 -0
  7. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/chatbot/docs_summary.py +2 -2
  8. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/chatbot/indexer.py +3 -3
  9. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/chatbot/linking.py +2 -2
  10. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/chatbot/live_fallback_mixin.py +1 -2
  11. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/chatbot/retrieval_mixin.py +1 -38
  12. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/chatbot/service.py +8 -49
  13. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/chatbot/settings.py +2 -11
  14. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/cli.py +11 -8
  15. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/generator/__init__.py +0 -12
  16. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/generator/evidence.py +18 -9
  17. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/generator/generation.py +147 -119
  18. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/generator/post_processors.py +149 -373
  19. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/generator/validation.py +122 -51
  20. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/persistence_v2.py +32 -6
  21. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/pipeline_v2.py +181 -70
  22. deepdoc-2.3.0/deepdoc/planner/__init__.py +3 -0
  23. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/planner/bucket_injection.py +147 -298
  24. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/planner/bucket_refinement.py +169 -83
  25. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/planner/common.py +21 -232
  26. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/planner/engine.py +43 -9
  27. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/planner/heuristics.py +13 -150
  28. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/planner/nav_shaping.py +80 -70
  29. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/planner/specializations.py +154 -16
  30. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/planner/topology.py +25 -8
  31. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/planner/utils.py +0 -13
  32. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/prompts/system.py +61 -54
  33. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/scanner/artifacts.py +17 -5
  34. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/scanner/clustering.py +6 -2
  35. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/scanner/database.py +9 -2
  36. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/scanner/endpoints.py +3 -3
  37. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/scanner/integrations.py +3 -1
  38. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/scanner/runtime.py +8 -0
  39. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/scanner/utils.py +4 -0
  40. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/site/builder/__init__.py +2 -2
  41. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/site/builder/engine.py +108 -45
  42. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/site/builder/mdx_utils.py +4 -4
  43. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/site/builder/scaffold_files.py +127 -1
  44. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/site/builder/templates.py +1 -0
  45. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/smart_update_v2.py +4 -4
  46. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/updater_v2.py +4 -4
  47. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/v2_models.py +0 -3
  48. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc.egg-info/PKG-INFO +13 -1
  49. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc.egg-info/SOURCES.txt +1 -7
  50. {deepdoc-2.2.0 → deepdoc-2.3.0}/pyproject.toml +1 -1
  51. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_changelog.py +6 -6
  52. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_chatbot_index.py +11 -11
  53. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_cli_serve.py +2 -2
  54. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_fumadocs_builder.py +26 -405
  55. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_generation_evidence.py +108 -8
  56. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_internal_docs_metadata.py +10 -11
  57. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_llm_json_utils.py +1 -1
  58. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_parallel_pipeline.py +162 -7
  59. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_planner_consolidation.py +2 -7
  60. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_planner_granularity.py +130 -75
  61. deepdoc-2.2.0/deepdoc/_legacy_types.py +0 -48
  62. deepdoc-2.2.0/deepdoc/generator/mdx_compile_gate.py +0 -383
  63. deepdoc-2.2.0/deepdoc/generator/mdx_validator/__init__.py +0 -182
  64. deepdoc-2.2.0/deepdoc/generator/mdx_validator/package.json +0 -12
  65. deepdoc-2.2.0/deepdoc/generator/mdx_validator/validate.mjs +0 -53
  66. deepdoc-2.2.0/deepdoc/planner/__init__.py +0 -8
  67. deepdoc-2.2.0/deepdoc/prompts_v2.py +0 -43
  68. deepdoc-2.2.0/tests/test_mdx_compile_gate.py +0 -285
  69. {deepdoc-2.2.0 → deepdoc-2.3.0}/LICENSE +0 -0
  70. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/__main__.py +0 -0
  71. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/benchmark_v2.py +0 -0
  72. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/call_graph.py +0 -0
  73. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/chatbot/__init__.py +0 -0
  74. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/chatbot/chunker.py +0 -0
  75. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/chatbot/deep_research.py +0 -0
  76. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/chatbot/embeddings.py +0 -0
  77. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/chatbot/persistence.py +0 -0
  78. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/chatbot/providers.py +0 -0
  79. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/chatbot/routes.py +0 -0
  80. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/chatbot/scaffold.py +0 -0
  81. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/chatbot/source_archive.py +0 -0
  82. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/chatbot/symbol_index.py +0 -0
  83. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/chatbot/types.py +0 -0
  84. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/config.py +0 -0
  85. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/llm/__init__.py +0 -0
  86. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/llm/client.py +0 -0
  87. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/llm/json_utils.py +0 -0
  88. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/llm/litellm_compat.py +0 -0
  89. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/manifest.py +0 -0
  90. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/openapi.py +0 -0
  91. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/parser/__init__.py +0 -0
  92. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/parser/api_detector.py +0 -0
  93. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/parser/base.py +0 -0
  94. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/parser/go_parser.py +0 -0
  95. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/parser/js_ts_parser.py +0 -0
  96. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/parser/php_parser.py +0 -0
  97. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/parser/python_parser.py +0 -0
  98. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/parser/registry.py +0 -0
  99. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/parser/routes/__init__.py +0 -0
  100. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/parser/routes/base.py +0 -0
  101. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/parser/routes/common.py +0 -0
  102. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/parser/routes/detector.py +0 -0
  103. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/parser/routes/django.py +0 -0
  104. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/parser/routes/express.py +0 -0
  105. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/parser/routes/falcon.py +0 -0
  106. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/parser/routes/fastify.py +0 -0
  107. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/parser/routes/go.py +0 -0
  108. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/parser/routes/js_shared.py +0 -0
  109. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/parser/routes/laravel.py +0 -0
  110. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/parser/routes/nestjs.py +0 -0
  111. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/parser/routes/python_shared.py +0 -0
  112. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/parser/routes/registry.py +0 -0
  113. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/parser/routes/repo_resolver.py +0 -0
  114. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/parser/vue_parser.py +0 -0
  115. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/planner/endpoint_refs.py +0 -0
  116. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/planner/flow_candidates.py +0 -0
  117. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/prompts/__init__.py +0 -0
  118. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/prompts/bucket_types.py +0 -0
  119. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/prompts/page_types.py +0 -0
  120. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/prompts/selectors.py +0 -0
  121. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/prompts/update.py +0 -0
  122. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/py.typed +0 -0
  123. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/scanner/__init__.py +0 -0
  124. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/scanner/common.py +0 -0
  125. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/site/__init__.py +0 -0
  126. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/site/builder/chatbot_components.py +0 -0
  127. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/site/builder/common.py +0 -0
  128. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc/source_metadata.py +0 -0
  129. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc.egg-info/dependency_links.txt +0 -0
  130. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc.egg-info/entry_points.txt +0 -0
  131. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc.egg-info/requires.txt +0 -0
  132. {deepdoc-2.2.0 → deepdoc-2.3.0}/deepdoc.egg-info/top_level.txt +0 -0
  133. {deepdoc-2.2.0 → deepdoc-2.3.0}/setup.cfg +0 -0
  134. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_benchmark_scorecard.py +0 -0
  135. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_call_graph.py +0 -0
  136. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_chatbot_config.py +0 -0
  137. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_chatbot_embeddings.py +0 -0
  138. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_chatbot_eval.py +0 -0
  139. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_chatbot_persistence.py +0 -0
  140. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_chatbot_providers.py +0 -0
  141. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_chatbot_query.py +0 -0
  142. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_chatbot_relationship.py +0 -0
  143. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_chatbot_scaffold.py +0 -0
  144. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_chatbot_source_archive.py +0 -0
  145. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_classify.py +0 -0
  146. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_cli_generate.py +0 -0
  147. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_cli_update.py +0 -0
  148. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_flow_candidates.py +0 -0
  149. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_framework_fixtures.py +0 -0
  150. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_framework_support.py +0 -0
  151. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_litellm_compat.py +0 -0
  152. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_parser_ranges.py +0 -0
  153. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_route_registry.py +0 -0
  154. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_runtime_scan.py +0 -0
  155. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_smart_update.py +0 -0
  156. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_stale.py +0 -0
  157. {deepdoc-2.2.0 → deepdoc-2.3.0}/tests/test_state.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepdoc
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: Auto-generate beautiful docs from any codebase
5
5
  Author: Pranav Kumar
6
6
  License: MIT
@@ -43,6 +43,18 @@ Dynamic: license-file
43
43
  [![Python versions](https://img.shields.io/pypi/pyversions/deepdoc)](https://pypi.org/project/deepdoc/)
44
44
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
45
45
 
46
+ ## Repository Layout
47
+
48
+ | Directory | What it is | Where to start |
49
+ |---|---|---|
50
+ | [`deepdoc/`](./deepdoc/) | The Python package — CLI, pipeline, planner, generator, chatbot, and site builder. This is the core product. | [`deepdoc/README.md`](./deepdoc/README.md) |
51
+ | [`web/`](./web/) | Marketing and changelog site built with Astro 5 + Tailwind. Deployed to the public DeepDoc website. | [`web/README.md`](./web/README.md) |
52
+ | [`vscode-extension/`](./vscode-extension/) | VS Code extension — explains selected code snippets in Fast or Deep mode and inserts AI-generated comments inline. | [`vscode-extension/README.md`](./vscode-extension/README.md) |
53
+ | [`tests/`](./tests/) | pytest test suite for the Python package. | Run `python3 -m pytest -q` from repo root. |
54
+ | [`scripts/`](./scripts/) | One-off release and maintenance scripts. | — |
55
+
56
+ ---
57
+
46
58
  Auto-generate deep engineering documentation from real codebases using AI.
47
59
 
48
60
  DeepDoc scans your repo, builds a bucket-based documentation plan, generates rich MDX pages with Mermaid diagrams, and builds a local-first Fumadocs site with Orama search.
@@ -4,6 +4,18 @@
4
4
  [![Python versions](https://img.shields.io/pypi/pyversions/deepdoc)](https://pypi.org/project/deepdoc/)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
6
6
 
7
+ ## Repository Layout
8
+
9
+ | Directory | What it is | Where to start |
10
+ |---|---|---|
11
+ | [`deepdoc/`](./deepdoc/) | The Python package — CLI, pipeline, planner, generator, chatbot, and site builder. This is the core product. | [`deepdoc/README.md`](./deepdoc/README.md) |
12
+ | [`web/`](./web/) | Marketing and changelog site built with Astro 5 + Tailwind. Deployed to the public DeepDoc website. | [`web/README.md`](./web/README.md) |
13
+ | [`vscode-extension/`](./vscode-extension/) | VS Code extension — explains selected code snippets in Fast or Deep mode and inserts AI-generated comments inline. | [`vscode-extension/README.md`](./vscode-extension/README.md) |
14
+ | [`tests/`](./tests/) | pytest test suite for the Python package. | Run `python3 -m pytest -q` from repo root. |
15
+ | [`scripts/`](./scripts/) | One-off release and maintenance scripts. | — |
16
+
17
+ ---
18
+
7
19
  Auto-generate deep engineering documentation from real codebases using AI.
8
20
 
9
21
  DeepDoc scans your repo, builds a bucket-based documentation plan, generates rich MDX pages with Mermaid diagrams, and builds a local-first Fumadocs site with Orama search.
@@ -1,3 +1,3 @@
1
1
  """DeepDoc — Auto-generate beautiful docs from any codebase."""
2
2
 
3
- __version__ = "2.1.0"
3
+ __version__ = "2.2.1"
@@ -31,7 +31,7 @@ def record_and_write(
31
31
  files_changed: list[str],
32
32
  is_initial: bool = False,
33
33
  ) -> None:
34
- """Append one changelog entry and regenerate whats-changed.mdx."""
34
+ """Append one changelog entry and regenerate whats-changed.md."""
35
35
  entry = {
36
36
  "commit": commit[:8],
37
37
  "run_at": datetime.now(timezone.utc).isoformat(timespec="seconds"),
@@ -47,15 +47,15 @@ def record_and_write(
47
47
 
48
48
 
49
49
  def write_whats_changed_page(repo_root: Path, output_dir: Path) -> None:
50
- """Write docs/whats-changed.mdx from .deepdoc/changelog.json."""
50
+ """Write docs/whats-changed.md from .deepdoc/changelog.json."""
51
51
  entries = load_changelog(repo_root)
52
- mdx = _build_mdx(entries)
52
+ content = _build_md(entries)
53
53
  output_dir.mkdir(parents=True, exist_ok=True)
54
- atomic_write_text(output_dir / "whats-changed.mdx", mdx)
54
+ atomic_write_text(output_dir / "whats-changed.md", content)
55
55
  _ensure_in_nav(repo_root)
56
56
 
57
57
 
58
- def _build_mdx(entries: list[dict]) -> str:
58
+ def _build_md(entries: list[dict]) -> str:
59
59
  lines = [
60
60
  "---",
61
61
  'title: "What\'s Changed"',
@@ -72,11 +72,11 @@ def _build_mdx(entries: list[dict]) -> str:
72
72
 
73
73
  if not entries:
74
74
  lines.append(
75
- "<Callout>No changelog entries yet. Run `deepdoc generate` to create the first entry.</Callout>"
75
+ ":::note\nNo changelog entries yet. Run `deepdoc generate` to create the first entry.\n:::"
76
76
  )
77
77
  return "\n".join(lines)
78
78
 
79
- lines.append("<Accordions>")
79
+ lines.append(":::accordions")
80
80
  for entry in entries:
81
81
  date = entry.get("date", "")
82
82
  msg = entry.get("commit_message", "update")
@@ -88,7 +88,7 @@ def _build_mdx(entries: list[dict]) -> str:
88
88
  strategy_label = _STRATEGY_LABEL.get(strategy, strategy)
89
89
 
90
90
  title = f"{date} — {msg[:72]} ({sha})"
91
- lines.append(f'<Accordion title="{title}">')
91
+ lines.append(f'::accordion{{title="{title}"}}')
92
92
  lines.append("")
93
93
 
94
94
  # Commit metadata row
@@ -119,7 +119,7 @@ def _build_mdx(entries: list[dict]) -> str:
119
119
  lines.append(f"- [{_slug_to_title(s)}](/{s})")
120
120
  else:
121
121
  lines.append(
122
- "<Callout type='info'>No pages were regenerated — only metadata or chatbot corpora were refreshed.</Callout>"
122
+ ":::info\nNo pages were regenerated — only metadata or chatbot corpora were refreshed.\n:::"
123
123
  )
124
124
 
125
125
  # Source files that changed
@@ -148,9 +148,9 @@ def _build_mdx(entries: list[dict]) -> str:
148
148
  )
149
149
 
150
150
  lines.append("")
151
- lines.append("</Accordion>")
151
+ lines.append("::")
152
152
 
153
- lines.append("</Accordions>")
153
+ lines.append(":::")
154
154
  return "\n".join(lines)
155
155
 
156
156
 
@@ -7,6 +7,11 @@ import re
7
7
  from typing import Any, Callable
8
8
 
9
9
  from ..source_metadata import classify_source_kind
10
+ from .constants import (
11
+ CODE_WORKSPACE_CONFIG_NAMES,
12
+ CODE_WORKSPACE_CONFIG_SUFFIXES,
13
+ CODE_WORKSPACE_SUFFIXES,
14
+ )
10
15
  from .types import (
11
16
  EvidenceItem,
12
17
  ReferenceItem,
@@ -14,17 +19,6 @@ from .types import (
14
19
  RetrievedChunk,
15
20
  )
16
21
 
17
- CODE_WORKSPACE_SUFFIXES = {
18
- ".py", ".js", ".jsx", ".ts", ".tsx", ".go", ".php", ".java", ".rb",
19
- ".rs", ".vue", ".svelte", ".html", ".css", ".scss", ".sass",
20
- }
21
- CODE_WORKSPACE_CONFIG_NAMES = {
22
- ".env", ".env.example", "docker-compose.yml", "docker-compose.yaml",
23
- "package.json", "pyproject.toml", "requirements.txt", "composer.json",
24
- "go.mod", "cargo.toml", "gemfile",
25
- }
26
- CODE_WORKSPACE_CONFIG_SUFFIXES = {".json", ".toml", ".yaml", ".yml", ".ini", ".cfg"}
27
-
28
22
 
29
23
  class AnswerMixin:
30
24
  """Mixin providing answer generation and evidence contract methods."""
@@ -1039,7 +1033,7 @@ class AnswerMixin:
1039
1033
  {
1040
1034
  "title": page.title,
1041
1035
  "url": url,
1042
- "doc_path": f"{page.slug}.mdx",
1036
+ "doc_path": f"{page.slug}.md",
1043
1037
  },
1044
1038
  )
1045
1039
  return list(links.values())[:5]
@@ -0,0 +1,27 @@
1
+ """Shared constants for the chatbot module."""
2
+
3
+ from __future__ import annotations
4
+
5
+ STOPWORD_TOKENS: frozenset[str] = frozenset({
6
+ "a", "an", "and", "any", "are", "can", "does", "first", "for", "from",
7
+ "handle", "handled", "how", "in", "is", "it", "its", "of", "or", "repo",
8
+ "repository", "show", "that", "the", "this", "to", "use", "what", "went",
9
+ "where", "who", "which", "with", "work",
10
+ })
11
+
12
+ DOC_SUFFIXES: frozenset[str] = frozenset({".md", ".mdx", ".txt", ".rst", ".adoc", ".ipynb"})
13
+
14
+ CODE_WORKSPACE_SUFFIXES: frozenset[str] = frozenset({
15
+ ".py", ".js", ".jsx", ".ts", ".tsx", ".go", ".php", ".java", ".rb",
16
+ ".rs", ".vue", ".svelte", ".html", ".css", ".scss", ".sass",
17
+ })
18
+
19
+ CODE_WORKSPACE_CONFIG_NAMES: frozenset[str] = frozenset({
20
+ ".env", ".env.example", "docker-compose.yml", "docker-compose.yaml",
21
+ "package.json", "pyproject.toml", "requirements.txt", "composer.json",
22
+ "go.mod", "cargo.toml", "gemfile",
23
+ })
24
+
25
+ CODE_WORKSPACE_CONFIG_SUFFIXES: frozenset[str] = frozenset({
26
+ ".json", ".toml", ".yaml", ".yml", ".ini", ".cfg",
27
+ })
@@ -548,8 +548,8 @@ def _looks_binary(path: Path) -> bool:
548
548
  def _doc_path_for_page(output_dir: Path, page: Any) -> Path:
549
549
  hints = (page._b.generation_hints or {}) if hasattr(page, "_b") else {}
550
550
  if hints.get("is_introduction_page") or page.page_type == "overview":
551
- return output_dir / "index.mdx"
552
- return output_dir / f"{page.slug}.mdx"
551
+ return output_dir / "index.md"
552
+ return output_dir / f"{page.slug}.md"
553
553
 
554
554
 
555
555
  def _doc_url(page: Any, has_openapi: bool) -> str:
@@ -316,14 +316,14 @@ class ChatbotIndexer:
316
316
  changed_keys=artifact_targets,
317
317
  deleted_keys=deleted_files,
318
318
  )
319
- deleted_doc_paths = [path for path in deleted_files if path.endswith(".mdx")]
319
+ deleted_doc_paths = [path for path in deleted_files if path.endswith((".md", ".mdx"))]
320
320
  if rebuild_doc_summary:
321
321
  self._save_records("doc_summary", doc_summary_records)
322
322
  else:
323
323
  self._merge_records(
324
324
  "doc_summary",
325
325
  doc_summary_records,
326
- changed_keys=[f"{slug}.mdx" for slug in changed_doc_slugs],
326
+ changed_keys=[f"{slug}.md" for slug in changed_doc_slugs],
327
327
  deleted_keys=deleted_doc_paths,
328
328
  )
329
329
  if rebuild_doc_full:
@@ -332,7 +332,7 @@ class ChatbotIndexer:
332
332
  self._merge_records(
333
333
  "doc_full",
334
334
  doc_full_records,
335
- changed_keys=[f"{slug}.mdx" for slug in changed_doc_slugs],
335
+ changed_keys=[f"{slug}.md" for slug in changed_doc_slugs],
336
336
  deleted_keys=deleted_doc_paths,
337
337
  )
338
338
  if rebuild_repo_doc:
@@ -67,8 +67,8 @@ def bucket_doc_path(bucket: DocBucket) -> str:
67
67
  hints = bucket.generation_hints or {}
68
68
  page_type = hints.get("prompt_style", bucket.bucket_type)
69
69
  if hints.get("is_introduction_page") or page_type == "overview":
70
- return "index.mdx"
71
- return f"{bucket.slug}.mdx"
70
+ return "index.md"
71
+ return f"{bucket.slug}.md"
72
72
 
73
73
 
74
74
  def bucket_doc_url(bucket: DocBucket, *, has_openapi: bool = False) -> str:
@@ -8,13 +8,12 @@ from pathlib import Path
8
8
  from typing import Any
9
9
 
10
10
  from ..source_metadata import classify_source_kind
11
+ from .constants import DOC_SUFFIXES
11
12
  from .types import (
12
13
  ChunkRecord,
13
14
  RetrievedChunk,
14
15
  )
15
16
 
16
- DOC_SUFFIXES = {".md", ".mdx", ".txt", ".rst", ".adoc", ".ipynb"}
17
-
18
17
 
19
18
  class LiveFallbackMixin:
20
19
  """Mixin providing live-repo fallback search methods."""
@@ -7,6 +7,7 @@ from pathlib import Path
7
7
  import re
8
8
  from typing import Any
9
9
 
10
+ from .constants import DOC_SUFFIXES, STOPWORD_TOKENS
10
11
  from .persistence import (
11
12
  query_lexical_index,
12
13
  similarity_search,
@@ -15,44 +16,6 @@ from .types import (
15
16
  RetrievedChunk,
16
17
  )
17
18
 
18
- STOPWORD_TOKENS = {
19
- "a",
20
- "an",
21
- "and",
22
- "any",
23
- "are",
24
- "can",
25
- "does",
26
- "first",
27
- "for",
28
- "from",
29
- "handle",
30
- "handled",
31
- "how",
32
- "in",
33
- "is",
34
- "it",
35
- "its",
36
- "of",
37
- "or",
38
- "repo",
39
- "repository",
40
- "show",
41
- "that",
42
- "the",
43
- "this",
44
- "to",
45
- "use",
46
- "what",
47
- "went",
48
- "where",
49
- "who",
50
- "which",
51
- "with",
52
- "work",
53
- }
54
- DOC_SUFFIXES = {".md", ".mdx", ".txt", ".rst", ".adoc", ".ipynb"}
55
-
56
19
 
57
20
  class RetrievalMixin:
58
21
  """Mixin providing retrieval and search methods for ChatbotQueryService."""
@@ -39,59 +39,18 @@ from .types import (
39
39
  SourceCatalogEntry,
40
40
  )
41
41
 
42
- from .retrieval_mixin import RetrievalMixin
43
42
  from .answer_mixin import AnswerMixin
43
+ from .constants import (
44
+ CODE_WORKSPACE_CONFIG_NAMES,
45
+ CODE_WORKSPACE_CONFIG_SUFFIXES,
46
+ CODE_WORKSPACE_SUFFIXES,
47
+ DOC_SUFFIXES,
48
+ STOPWORD_TOKENS,
49
+ )
44
50
  from .live_fallback_mixin import LiveFallbackMixin
51
+ from .retrieval_mixin import RetrievalMixin
45
52
  from .routes import create_fastapi_app, QueryRequest, DeepResearchRequest, CodeDeepRequest
46
53
 
47
- STOPWORD_TOKENS = {
48
- "a",
49
- "an",
50
- "and",
51
- "any",
52
- "are",
53
- "can",
54
- "does",
55
- "first",
56
- "for",
57
- "from",
58
- "handle",
59
- "handled",
60
- "how",
61
- "in",
62
- "is",
63
- "it",
64
- "its",
65
- "of",
66
- "or",
67
- "repo",
68
- "repository",
69
- "show",
70
- "that",
71
- "the",
72
- "this",
73
- "to",
74
- "use",
75
- "what",
76
- "went",
77
- "where",
78
- "who",
79
- "which",
80
- "with",
81
- "work",
82
- }
83
- DOC_SUFFIXES = {".md", ".mdx", ".txt", ".rst", ".adoc", ".ipynb"}
84
- CODE_WORKSPACE_SUFFIXES = {
85
- ".py", ".js", ".jsx", ".ts", ".tsx", ".go", ".php", ".java", ".rb",
86
- ".rs", ".vue", ".svelte", ".html", ".css", ".scss", ".sass",
87
- }
88
- CODE_WORKSPACE_CONFIG_NAMES = {
89
- ".env", ".env.example", "docker-compose.yml", "docker-compose.yaml",
90
- "package.json", "pyproject.toml", "requirements.txt", "composer.json",
91
- "go.mod", "cargo.toml", "gemfile",
92
- }
93
- CODE_WORKSPACE_CONFIG_SUFFIXES = {".json", ".toml", ".yaml", ".yml", ".ini", ".cfg"}
94
-
95
54
 
96
55
  class ChatbotQueryService(RetrievalMixin, AnswerMixin, LiveFallbackMixin):
97
56
  """Query all chatbot corpora and answer with grounded citations."""
@@ -2,13 +2,14 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from copy import deepcopy
6
5
  import os
7
6
  from pathlib import Path
8
7
  from typing import Any
9
8
  from urllib.parse import urlparse
10
9
  import zlib
11
10
 
11
+ from ..config import _deep_merge
12
+
12
13
  DEFAULT_CHATBOT_CONFIG: dict[str, Any] = {
13
14
  "enabled": False,
14
15
  "index_dir": ".deepdoc/chatbot",
@@ -113,16 +114,6 @@ DEFAULT_CHATBOT_CONFIG: dict[str, Any] = {
113
114
  }
114
115
 
115
116
 
116
- def _deep_merge(base: dict[str, Any], override: dict[str, Any]) -> dict[str, Any]:
117
- merged = deepcopy(base)
118
- for key, value in override.items():
119
- if isinstance(value, dict) and isinstance(merged.get(key), dict):
120
- merged[key] = _deep_merge(merged[key], value)
121
- else:
122
- merged[key] = value
123
- return merged
124
-
125
-
126
117
  def get_chatbot_cfg(cfg: dict[str, Any]) -> dict[str, Any]:
127
118
  return _deep_merge(DEFAULT_CHATBOT_CONFIG, cfg.get("chatbot", {}))
128
119
 
@@ -200,8 +200,10 @@ def init(name, description, provider, model, output_dir, with_chatbot):
200
200
  "openai": ("gpt-4o", "OPENAI_API_KEY"),
201
201
  "ollama": ("ollama/llama3.2", None),
202
202
  "azure": ("azure/gpt-4o", "AZURE_API_KEY"),
203
+ "google": ("gemini/gemini-1.5-pro", "GEMINI_API_KEY"),
204
+ "gemini": ("gemini/gemini-1.5-pro", "GEMINI_API_KEY"),
203
205
  }
204
- default_model, default_key_env = provider_defaults.get(provider, ("", ""))
206
+ default_model, default_key_env = provider_defaults.get(provider, ("", "DEEPDOC_LLM_API_KEY"))
205
207
  resolved_model = model or default_model
206
208
 
207
209
  cfg = dict(DEFAULT_CONFIG)
@@ -261,14 +263,15 @@ def init(name, description, provider, model, output_dir, with_chatbot):
261
263
  )
262
264
  next_steps.append(" 4. Generate docs: [bold]deepdoc generate[/bold]")
263
265
  next_steps.append(" 5. Preview locally: [bold]deepdoc serve[/bold]")
264
- elif cfg["llm"]["api_key_env"]:
265
- next_steps.append(
266
- f" 2. Set your API key: [bold]export {cfg['llm']['api_key_env']}=...[/bold]"
267
- )
266
+ elif provider == "ollama":
267
+ next_steps.append(" 2. Make sure Ollama is running locally")
268
268
  next_steps.append(" 3. Generate docs: [bold]deepdoc generate[/bold]")
269
269
  next_steps.append(" 4. Preview locally: [bold]deepdoc serve[/bold]")
270
270
  else:
271
- next_steps.append(" 2. Make sure Ollama is running locally")
271
+ key_env = cfg["llm"].get("api_key_env") or "DEEPDOC_LLM_API_KEY"
272
+ next_steps.append(
273
+ f" 2. Set your API key: [bold]export {key_env}=...[/bold]"
274
+ )
272
275
  next_steps.append(" 3. Generate docs: [bold]deepdoc generate[/bold]")
273
276
  next_steps.append(" 4. Preview locally: [bold]deepdoc serve[/bold]")
274
277
  if with_chatbot:
@@ -1320,7 +1323,7 @@ def _detect_generated_deepdoc_version(repo_root: Path, output_dir: Path) -> str
1320
1323
  for directory in candidates:
1321
1324
  if not directory.exists():
1322
1325
  continue
1323
- for doc_path in sorted(directory.rglob("*.mdx")):
1326
+ for doc_path in sorted(directory.rglob("*.md")):
1324
1327
  try:
1325
1328
  content = doc_path.read_text(encoding="utf-8", errors="replace")
1326
1329
  except OSError:
@@ -1456,7 +1459,7 @@ def _deployment_quality_blockers(repo_root: Path, output_dir: Path) -> list[str]
1456
1459
  if output_dir.exists():
1457
1460
  invalid_pages: list[str] = []
1458
1461
  stub_pages: list[str] = []
1459
- for doc_path in sorted(output_dir.rglob("*.mdx")):
1462
+ for doc_path in sorted(output_dir.rglob("*.md")):
1460
1463
  try:
1461
1464
  content = doc_path.read_text(encoding="utf-8")
1462
1465
  except Exception:
@@ -6,28 +6,16 @@ from .generation import (
6
6
  summarize_generation_results,
7
7
  BucketGenerationEngine,
8
8
  )
9
- from .mdx_compile_gate import GateOutcome, apply_mdx_compile_gate
10
- from .mdx_validator import (
11
- MdxCompileError,
12
- ValidationOutcome,
13
- bootstrap_validator,
14
- ensure_node_available,
15
- validate_mdx,
16
- )
17
9
  from .validation import ValidationResult, PageValidator
18
10
  from .post_processors import (
19
11
  _fix_mermaid_diagram,
20
12
  build_internal_doc_link_maps,
21
- escape_mdx_route_params,
22
- escape_mdx_text_hazards,
23
13
  fix_file_references,
24
14
  fix_mermaid_diagrams,
25
15
  normalize_code_fence_languages,
26
16
  normalize_explanatory_lines_outside_fences,
27
17
  normalize_html_code_blocks,
28
- normalize_mdx_steps,
29
18
  repair_internal_doc_links,
30
- repair_mdx_component_blocks,
31
19
  repair_split_object_code_fences,
32
20
  repair_dangling_plain_fences,
33
21
  repair_unbalanced_code_fences,
@@ -34,12 +34,17 @@ from ..llm import LLMClient
34
34
  from ..parser import parse_file, supported_extensions
35
35
  from ..parser.base import ParsedFile, Symbol
36
36
  from ..planner import DocBucket, DocPlan, RepoScan, tracked_bucket_files
37
- from ..prompts_v2 import SYSTEM_V2, get_prompt_for_bucket
37
+ from ..prompts import SYSTEM_V2, get_prompt_for_bucket
38
38
  from ..scanner import _classify_file_role
39
39
  from ..openapi import parse_openapi_spec, spec_to_context_string
40
40
 
41
41
  console = Console()
42
42
 
43
+ _EP_TITLE_RE = re.compile(
44
+ r"^(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS|CONNECT|TRACE)\s+(/\S*)",
45
+ re.IGNORECASE,
46
+ )
47
+
43
48
  # ═════════════════════════════════════════════════════════════════════════════
44
49
  # 3.1 Evidence Assembly
45
50
  # ═════════════════════════════════════════════════════════════════════════════
@@ -67,6 +72,7 @@ class AssembledEvidence:
67
72
  compressed_cards_context: str = ""
68
73
  files_included_raw: int = 0
69
74
  files_compressed: int = 0
75
+ compressed_file_paths: set[str] = field(default_factory=set)
70
76
  coverage_files_total: int = 0
71
77
  helper_context: str = "" # resolved helper/utility function bodies
72
78
  flow_context: str = "" # call graph + flow evidence (entrypoints, chains, side effects)
@@ -141,7 +147,7 @@ class EvidenceAssembler:
141
147
  source_ctx,
142
148
  compressed_cards_ctx,
143
149
  files_included_raw,
144
- files_compressed,
150
+ compressed_file_paths,
145
151
  coverage_total,
146
152
  ) = self._build_source_context(bucket)
147
153
  endpoints_detail = self._build_endpoints_detail(bucket)
@@ -204,7 +210,8 @@ class EvidenceAssembler:
204
210
  flow_context=flow_ctx,
205
211
  total_evidence_chars=total,
206
212
  files_included_raw=files_included_raw,
207
- files_compressed=files_compressed,
213
+ files_compressed=len(compressed_file_paths),
214
+ compressed_file_paths=compressed_file_paths,
208
215
  coverage_files_total=coverage_total,
209
216
  evidence_file_paths=evidence_files,
210
217
  config_env_context=config_env_ctx,
@@ -316,11 +323,12 @@ class EvidenceAssembler:
316
323
  included += 1
317
324
 
318
325
  cards_context = self._format_compressed_cards(compressed_cards)
326
+ compressed_paths = {card.file_path for card in compressed_cards}
319
327
  return (
320
328
  "\n".join(parts),
321
329
  cards_context,
322
330
  included,
323
- len(compressed_cards),
331
+ compressed_paths,
324
332
  len(ranked_files),
325
333
  )
326
334
 
@@ -780,7 +788,7 @@ class EvidenceAssembler:
780
788
  """Extract actual env var names from source files for grounded config docs."""
781
789
  env_vars: dict[str, list[str]] = {} # var_name -> [file_paths]
782
790
 
783
- for src_file in bucket.owned_files:
791
+ for src_file in list(bucket.owned_files) + list(bucket.artifact_refs or []):
784
792
  src_path = self.repo_root / src_file
785
793
  if not src_path.exists():
786
794
  continue
@@ -1052,10 +1060,11 @@ class EvidenceAssembler:
1052
1060
 
1053
1061
  # ── endpoint_ref: match specific endpoint, pull deep evidence ─────
1054
1062
  if hints.get("is_endpoint_ref"):
1055
- # The title is e.g. "GET /api/v1/orders" — extract method+path
1056
- title_parts = bucket.title.split(" ", 1)
1057
- ref_method = title_parts[0].upper() if len(title_parts) >= 1 else ""
1058
- ref_path = title_parts[1] if len(title_parts) >= 2 else ""
1063
+ # Extract METHOD /path from title via regex — more robust than split(" ", 1)
1064
+ # since titles may not always follow the "GET /path" convention.
1065
+ _m = _EP_TITLE_RE.match(bucket.title)
1066
+ ref_method = _m.group(1).upper() if _m else ""
1067
+ ref_path = _m.group(2) if _m else ""
1059
1068
 
1060
1069
  # Find matching bundle via handler symbol or method+path
1061
1070
  matched_bundle = None