chunkhound 5.0.0__py3-none-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (383) hide show
  1. chunkhound/__init__.py +27 -0
  2. chunkhound/_version.py +24 -0
  3. chunkhound/api/__init__.py +5 -0
  4. chunkhound/api/cli/__init__.py +9 -0
  5. chunkhound/api/cli/commands/__init__.py +12 -0
  6. chunkhound/api/cli/commands/autodoc.py +104 -0
  7. chunkhound/api/cli/commands/autodoc_autorun.py +464 -0
  8. chunkhound/api/cli/commands/autodoc_cleanup.py +186 -0
  9. chunkhound/api/cli/commands/autodoc_errors.py +11 -0
  10. chunkhound/api/cli/commands/autodoc_generate.py +107 -0
  11. chunkhound/api/cli/commands/autodoc_git.py +70 -0
  12. chunkhound/api/cli/commands/autodoc_prompts.py +74 -0
  13. chunkhound/api/cli/commands/calibrate.py +197 -0
  14. chunkhound/api/cli/commands/code_mapper.py +372 -0
  15. chunkhound/api/cli/commands/daemon.py +45 -0
  16. chunkhound/api/cli/commands/mcp.py +240 -0
  17. chunkhound/api/cli/commands/research.py +119 -0
  18. chunkhound/api/cli/commands/run.py +833 -0
  19. chunkhound/api/cli/commands/search.py +220 -0
  20. chunkhound/api/cli/env_detector.py +256 -0
  21. chunkhound/api/cli/keyboard.py +184 -0
  22. chunkhound/api/cli/main.py +258 -0
  23. chunkhound/api/cli/parsers/__init__.py +14 -0
  24. chunkhound/api/cli/parsers/autodoc_parser.py +202 -0
  25. chunkhound/api/cli/parsers/calibrate_parser.py +84 -0
  26. chunkhound/api/cli/parsers/code_mapper_parser.py +195 -0
  27. chunkhound/api/cli/parsers/common_arguments.py +75 -0
  28. chunkhound/api/cli/parsers/daemon_parser.py +41 -0
  29. chunkhound/api/cli/parsers/main_parser.py +55 -0
  30. chunkhound/api/cli/parsers/mcp_parser.py +52 -0
  31. chunkhound/api/cli/parsers/research_parser.py +61 -0
  32. chunkhound/api/cli/parsers/run_parser.py +231 -0
  33. chunkhound/api/cli/parsers/search_parser.py +91 -0
  34. chunkhound/api/cli/setup_wizard.py +2032 -0
  35. chunkhound/api/cli/terminal/__init__.py +224 -0
  36. chunkhound/api/cli/terminal/core.py +146 -0
  37. chunkhound/api/cli/terminal/exceptions.py +184 -0
  38. chunkhound/api/cli/terminal/filters.py +66 -0
  39. chunkhound/api/cli/terminal/providers/__init__.py +1 -0
  40. chunkhound/api/cli/terminal/providers/base.py +223 -0
  41. chunkhound/api/cli/terminal/providers/unix.py +337 -0
  42. chunkhound/api/cli/terminal/providers/windows.py +145 -0
  43. chunkhound/api/cli/utils/__init__.py +20 -0
  44. chunkhound/api/cli/utils/code_mapper.py +56 -0
  45. chunkhound/api/cli/utils/config_factory.py +54 -0
  46. chunkhound/api/cli/utils/database.py +29 -0
  47. chunkhound/api/cli/utils/rich_output.py +666 -0
  48. chunkhound/api/cli/utils/text_input.py +130 -0
  49. chunkhound/api/cli/utils/tree_progress.py +225 -0
  50. chunkhound/api/cli/utils/validation.py +197 -0
  51. chunkhound/autodoc/__init__.py +0 -0
  52. chunkhound/autodoc/assets/README.md +11 -0
  53. chunkhound/autodoc/assets/__init__.py +5 -0
  54. chunkhound/autodoc/assets/astro.config.mjs +13 -0
  55. chunkhound/autodoc/assets/package.json +17 -0
  56. chunkhound/autodoc/assets/public/favicon.ico +0 -0
  57. chunkhound/autodoc/assets/public/fonts/DMSerifDisplay-Italic.ttf +0 -0
  58. chunkhound/autodoc/assets/public/fonts/DMSerifDisplay-Regular.ttf +0 -0
  59. chunkhound/autodoc/assets/public/fonts/SourceSans3VF-Italic.ttf.woff2 +0 -0
  60. chunkhound/autodoc/assets/public/fonts/SourceSans3VF-Upright.ttf.woff2 +0 -0
  61. chunkhound/autodoc/assets/public/fonts/licenses/DMSerif-LICENSE.txt +93 -0
  62. chunkhound/autodoc/assets/public/fonts/licenses/DMSerif-OFL.txt +93 -0
  63. chunkhound/autodoc/assets/public/fonts/licenses/SourceSans3-LICENSE.md +93 -0
  64. chunkhound/autodoc/assets/src/layouts/DocLayout.astro +535 -0
  65. chunkhound/autodoc/assets/src/styles/global.css +670 -0
  66. chunkhound/autodoc/assets/tsconfig.json +6 -0
  67. chunkhound/autodoc/cleanup.py +293 -0
  68. chunkhound/autodoc/docsite.py +18 -0
  69. chunkhound/autodoc/generator.py +290 -0
  70. chunkhound/autodoc/ia.py +414 -0
  71. chunkhound/autodoc/index_loader.py +107 -0
  72. chunkhound/autodoc/markdown_utils.py +186 -0
  73. chunkhound/autodoc/models.py +79 -0
  74. chunkhound/autodoc/prompts/cleanup_system_v2.txt +7 -0
  75. chunkhound/autodoc/prompts/cleanup_user_end_user_v1.txt +58 -0
  76. chunkhound/autodoc/prompts/cleanup_user_v2.txt +53 -0
  77. chunkhound/autodoc/references.py +190 -0
  78. chunkhound/autodoc/site_writer.py +201 -0
  79. chunkhound/autodoc/site_writer_dirs.py +75 -0
  80. chunkhound/autodoc/site_writer_metadata.py +154 -0
  81. chunkhound/autodoc/site_writer_renderers.py +223 -0
  82. chunkhound/autodoc/template_loader.py +37 -0
  83. chunkhound/code_mapper/__init__.py +10 -0
  84. chunkhound/code_mapper/coverage.py +75 -0
  85. chunkhound/code_mapper/hyde.py +285 -0
  86. chunkhound/code_mapper/llm.py +86 -0
  87. chunkhound/code_mapper/metadata.py +179 -0
  88. chunkhound/code_mapper/models.py +111 -0
  89. chunkhound/code_mapper/orchestrator.py +165 -0
  90. chunkhound/code_mapper/pipeline.py +423 -0
  91. chunkhound/code_mapper/prompts/__init__.py +1 -0
  92. chunkhound/code_mapper/prompts/hyde_scope_prompt.md +28 -0
  93. chunkhound/code_mapper/prompts/hyde_scope_prompt_context.md +9 -0
  94. chunkhound/code_mapper/prompts/hyde_scope_prompt_operational.md +28 -0
  95. chunkhound/code_mapper/public_utils.py +106 -0
  96. chunkhound/code_mapper/render.py +170 -0
  97. chunkhound/code_mapper/scope.py +70 -0
  98. chunkhound/code_mapper/service.py +626 -0
  99. chunkhound/code_mapper/utils.py +9 -0
  100. chunkhound/code_mapper/writer.py +102 -0
  101. chunkhound/core/__init__.py +41 -0
  102. chunkhound/core/audience.py +35 -0
  103. chunkhound/core/config/__init__.py +33 -0
  104. chunkhound/core/config/config.py +397 -0
  105. chunkhound/core/config/database_config.py +184 -0
  106. chunkhound/core/config/embedding_config.py +579 -0
  107. chunkhound/core/config/embedding_factory.py +402 -0
  108. chunkhound/core/config/indexing_config.py +625 -0
  109. chunkhound/core/config/llm_config.py +992 -0
  110. chunkhound/core/config/mcp_config.py +68 -0
  111. chunkhound/core/config/openai_utils.py +47 -0
  112. chunkhound/core/config/research_config.py +520 -0
  113. chunkhound/core/config/settings_sources.py +453 -0
  114. chunkhound/core/config/voyageai_utils.py +20 -0
  115. chunkhound/core/constants.py +5 -0
  116. chunkhound/core/detection/__init__.py +5 -0
  117. chunkhound/core/detection/language_detector.py +135 -0
  118. chunkhound/core/diagnostics/__init__.py +16 -0
  119. chunkhound/core/diagnostics/batch_metrics.py +73 -0
  120. chunkhound/core/diagnostics/perf_analyzer.py +266 -0
  121. chunkhound/core/exceptions/__init__.py +38 -0
  122. chunkhound/core/exceptions/core.py +325 -0
  123. chunkhound/core/models/__init__.py +24 -0
  124. chunkhound/core/models/chunk.py +436 -0
  125. chunkhound/core/models/embedding.py +435 -0
  126. chunkhound/core/models/file.py +316 -0
  127. chunkhound/core/types/__init__.py +48 -0
  128. chunkhound/core/types/common.py +482 -0
  129. chunkhound/core/utils/__init__.py +27 -0
  130. chunkhound/core/utils/chunk_utils.py +20 -0
  131. chunkhound/core/utils/embedding_utils.py +70 -0
  132. chunkhound/core/utils/path_utils.py +121 -0
  133. chunkhound/core/utils/token_utils.py +133 -0
  134. chunkhound/daemon/__init__.py +5 -0
  135. chunkhound/daemon/client_manager.py +75 -0
  136. chunkhound/daemon/client_proxy.py +158 -0
  137. chunkhound/daemon/discovery.py +1366 -0
  138. chunkhound/daemon/ipc/__init__.py +84 -0
  139. chunkhound/daemon/ipc/codec.py +44 -0
  140. chunkhound/daemon/ipc/unix_socket.py +41 -0
  141. chunkhound/daemon/ipc/windows_pipe.py +58 -0
  142. chunkhound/daemon/process.py +46 -0
  143. chunkhound/daemon/server.py +446 -0
  144. chunkhound/database.py +462 -0
  145. chunkhound/database_factory.py +171 -0
  146. chunkhound/embeddings.py +231 -0
  147. chunkhound/file_discovery_cache.py +256 -0
  148. chunkhound/interfaces/__init__.py +11 -0
  149. chunkhound/interfaces/database_provider.py +456 -0
  150. chunkhound/interfaces/embedding_provider.py +367 -0
  151. chunkhound/interfaces/language_parser.py +306 -0
  152. chunkhound/interfaces/llm_provider.py +266 -0
  153. chunkhound/llm_manager.py +266 -0
  154. chunkhound/mcp_server/__init__.py +38 -0
  155. chunkhound/mcp_server/base.py +1165 -0
  156. chunkhound/mcp_server/common.py +334 -0
  157. chunkhound/mcp_server/status.py +57 -0
  158. chunkhound/mcp_server/stdio.py +440 -0
  159. chunkhound/mcp_server/tools.py +684 -0
  160. chunkhound/parser.py +153 -0
  161. chunkhound/parsers/_grammar_availability.py +12 -0
  162. chunkhound/parsers/chunk_splitter.py +607 -0
  163. chunkhound/parsers/concept_extractor.py +178 -0
  164. chunkhound/parsers/embedded_sql_detector.py +431 -0
  165. chunkhound/parsers/makefile_parser.py +225 -0
  166. chunkhound/parsers/mapping_adapter.py +395 -0
  167. chunkhound/parsers/mappings/__init__.py +88 -0
  168. chunkhound/parsers/mappings/_shared/css_family_helpers.py +70 -0
  169. chunkhound/parsers/mappings/_shared/js_family_extraction.py +274 -0
  170. chunkhound/parsers/mappings/_shared/js_query_patterns.py +75 -0
  171. chunkhound/parsers/mappings/base.py +527 -0
  172. chunkhound/parsers/mappings/bash.py +486 -0
  173. chunkhound/parsers/mappings/c.py +758 -0
  174. chunkhound/parsers/mappings/cpp.py +973 -0
  175. chunkhound/parsers/mappings/csharp.py +1018 -0
  176. chunkhound/parsers/mappings/css.py +252 -0
  177. chunkhound/parsers/mappings/dart.py +609 -0
  178. chunkhound/parsers/mappings/elixir.py +510 -0
  179. chunkhound/parsers/mappings/go.py +558 -0
  180. chunkhound/parsers/mappings/groovy.py +901 -0
  181. chunkhound/parsers/mappings/haskell.py +524 -0
  182. chunkhound/parsers/mappings/hcl.py +523 -0
  183. chunkhound/parsers/mappings/html.py +405 -0
  184. chunkhound/parsers/mappings/java.py +897 -0
  185. chunkhound/parsers/mappings/javascript.py +623 -0
  186. chunkhound/parsers/mappings/json.py +339 -0
  187. chunkhound/parsers/mappings/jsx.py +461 -0
  188. chunkhound/parsers/mappings/kotlin.py +849 -0
  189. chunkhound/parsers/mappings/lua.py +502 -0
  190. chunkhound/parsers/mappings/makefile.py +614 -0
  191. chunkhound/parsers/mappings/markdown.py +749 -0
  192. chunkhound/parsers/mappings/matlab.py +799 -0
  193. chunkhound/parsers/mappings/objc.py +724 -0
  194. chunkhound/parsers/mappings/pdf.py +646 -0
  195. chunkhound/parsers/mappings/php.py +786 -0
  196. chunkhound/parsers/mappings/python.py +988 -0
  197. chunkhound/parsers/mappings/rust.py +780 -0
  198. chunkhound/parsers/mappings/scss.py +339 -0
  199. chunkhound/parsers/mappings/sql.py +299 -0
  200. chunkhound/parsers/mappings/svelte.py +124 -0
  201. chunkhound/parsers/mappings/swift.py +863 -0
  202. chunkhound/parsers/mappings/text.py +402 -0
  203. chunkhound/parsers/mappings/toml.py +416 -0
  204. chunkhound/parsers/mappings/tsx.py +595 -0
  205. chunkhound/parsers/mappings/typescript.py +793 -0
  206. chunkhound/parsers/mappings/vue.py +302 -0
  207. chunkhound/parsers/mappings/vue_template.py +511 -0
  208. chunkhound/parsers/mappings/yaml.py +469 -0
  209. chunkhound/parsers/mappings/zig.py +445 -0
  210. chunkhound/parsers/parser_factory.py +739 -0
  211. chunkhound/parsers/rapid_yaml_parser.py +835 -0
  212. chunkhound/parsers/svelte_parser.py +207 -0
  213. chunkhound/parsers/twincat/__init__.py +6 -0
  214. chunkhound/parsers/twincat/common.lark +72 -0
  215. chunkhound/parsers/twincat/declarations.lark +224 -0
  216. chunkhound/parsers/twincat/exceptions.py +35 -0
  217. chunkhound/parsers/twincat/implementation.lark +252 -0
  218. chunkhound/parsers/twincat/twincat_mapping.py +219 -0
  219. chunkhound/parsers/twincat/twincat_parser.py +1707 -0
  220. chunkhound/parsers/twincat/xml_extractor.py +570 -0
  221. chunkhound/parsers/universal_engine.py +112 -0
  222. chunkhound/parsers/universal_parser.py +1068 -0
  223. chunkhound/parsers/vue_cross_ref.py +740 -0
  224. chunkhound/parsers/vue_parser.py +335 -0
  225. chunkhound/parsers/yaml_template_sanitizer.py +383 -0
  226. chunkhound/providers/__init__.py +11 -0
  227. chunkhound/providers/database/__init__.py +7 -0
  228. chunkhound/providers/database/duckdb/__init__.py +5 -0
  229. chunkhound/providers/database/duckdb/chunk_repository.py +468 -0
  230. chunkhound/providers/database/duckdb/connection_manager.py +375 -0
  231. chunkhound/providers/database/duckdb/embedding_repository.py +742 -0
  232. chunkhound/providers/database/duckdb/file_repository.py +400 -0
  233. chunkhound/providers/database/duckdb_provider.py +3747 -0
  234. chunkhound/providers/database/lancedb_provider.py +2435 -0
  235. chunkhound/providers/database/like_utils.py +18 -0
  236. chunkhound/providers/database/serial_database_provider.py +634 -0
  237. chunkhound/providers/database/serial_executor.py +258 -0
  238. chunkhound/providers/embeddings/README.md +90 -0
  239. chunkhound/providers/embeddings/__init__.py +7 -0
  240. chunkhound/providers/embeddings/batch_utils.py +244 -0
  241. chunkhound/providers/embeddings/openai_provider.py +1551 -0
  242. chunkhound/providers/embeddings/shared_utils.py +62 -0
  243. chunkhound/providers/embeddings/voyageai_provider.py +820 -0
  244. chunkhound/providers/llm/__init__.py +21 -0
  245. chunkhound/providers/llm/anthropic_llm_provider.py +1199 -0
  246. chunkhound/providers/llm/base_cli_provider.py +314 -0
  247. chunkhound/providers/llm/claude_code_cli_provider.py +184 -0
  248. chunkhound/providers/llm/codex_cli_provider.py +674 -0
  249. chunkhound/providers/llm/gemini_llm_provider.py +430 -0
  250. chunkhound/providers/llm/grok_llm_provider.py +60 -0
  251. chunkhound/providers/llm/openai_compatible_provider.py +343 -0
  252. chunkhound/providers/llm/openai_llm_provider.py +424 -0
  253. chunkhound/providers/llm/opencode_cli_provider.py +196 -0
  254. chunkhound/py.typed +0 -0
  255. chunkhound/registry/__init__.py +479 -0
  256. chunkhound/services/__init__.py +8 -0
  257. chunkhound/services/base_service.py +22 -0
  258. chunkhound/services/batch_processor.py +371 -0
  259. chunkhound/services/chunk_cache_service.py +89 -0
  260. chunkhound/services/clustering_service.py +684 -0
  261. chunkhound/services/deep_research_service.py +111 -0
  262. chunkhound/services/directory_indexing_service.py +171 -0
  263. chunkhound/services/embedding_service.py +992 -0
  264. chunkhound/services/indexing_coordinator.py +3124 -0
  265. chunkhound/services/prompts/__init__.py +31 -0
  266. chunkhound/services/prompts/followup_generation.py +25 -0
  267. chunkhound/services/prompts/query_expansion.py +29 -0
  268. chunkhound/services/prompts/question_filtering.py +21 -0
  269. chunkhound/services/prompts/question_synthesis.py +30 -0
  270. chunkhound/services/prompts/synthesis.py +224 -0
  271. chunkhound/services/realtime/__init__.py +51 -0
  272. chunkhound/services/realtime/adapters/__init__.py +17 -0
  273. chunkhound/services/realtime/adapters/polling.py +35 -0
  274. chunkhound/services/realtime/adapters/watchdog.py +39 -0
  275. chunkhound/services/realtime/adapters/watchman.py +1150 -0
  276. chunkhound/services/realtime/context.py +234 -0
  277. chunkhound/services/realtime/events.py +292 -0
  278. chunkhound/services/realtime/pipeline.py +1451 -0
  279. chunkhound/services/realtime/service.py +956 -0
  280. chunkhound/services/realtime/startup.py +774 -0
  281. chunkhound/services/realtime/task_supervision.py +28 -0
  282. chunkhound/services/realtime_indexing_service.py +11 -0
  283. chunkhound/services/realtime_path_filter.py +211 -0
  284. chunkhound/services/research/__init__.py +71 -0
  285. chunkhound/services/research/factory.py +221 -0
  286. chunkhound/services/research/models.py +59 -0
  287. chunkhound/services/research/protocol.py +33 -0
  288. chunkhound/services/research/query_expander.py +11 -0
  289. chunkhound/services/research/question_generator.py +11 -0
  290. chunkhound/services/research/schemas.py +41 -0
  291. chunkhound/services/research/shared/__init__.py +474 -0
  292. chunkhound/services/research/shared/budget_calculator.py +220 -0
  293. chunkhound/services/research/shared/chunk_context_builder.py +312 -0
  294. chunkhound/services/research/shared/chunk_dedup.py +110 -0
  295. chunkhound/services/research/shared/chunk_range.py +254 -0
  296. chunkhound/services/research/shared/citation_manager.py +414 -0
  297. chunkhound/services/research/shared/context_manager.py +257 -0
  298. chunkhound/services/research/shared/depth_exploration.py +515 -0
  299. chunkhound/services/research/shared/elbow_detection.py +156 -0
  300. chunkhound/services/research/shared/error_categorization.py +58 -0
  301. chunkhound/services/research/shared/evidence_ledger/__init__.py +54 -0
  302. chunkhound/services/research/shared/evidence_ledger/clustered_extractor.py +135 -0
  303. chunkhound/services/research/shared/evidence_ledger/extractor.py +251 -0
  304. chunkhound/services/research/shared/evidence_ledger/ledger.py +795 -0
  305. chunkhound/services/research/shared/evidence_ledger/models.py +112 -0
  306. chunkhound/services/research/shared/evidence_ledger/prompts.py +125 -0
  307. chunkhound/services/research/shared/exploration/__init__.py +60 -0
  308. chunkhound/services/research/shared/exploration/bfs_exploration_strategy.py +1036 -0
  309. chunkhound/services/research/shared/exploration/elbow_filter.py +131 -0
  310. chunkhound/services/research/shared/exploration/parallel_strategy.py +297 -0
  311. chunkhound/services/research/shared/exploration/protocol.py +74 -0
  312. chunkhound/services/research/shared/exploration/wide_coverage_strategy.py +264 -0
  313. chunkhound/services/research/shared/failure_tracker.py +105 -0
  314. chunkhound/services/research/shared/file_reader.py +223 -0
  315. chunkhound/services/research/shared/gap_detection.py +894 -0
  316. chunkhound/services/research/shared/gap_models.py +42 -0
  317. chunkhound/services/research/shared/import_context.py +151 -0
  318. chunkhound/services/research/shared/import_resolution_helper.py +151 -0
  319. chunkhound/services/research/shared/import_resolver.py +160 -0
  320. chunkhound/services/research/shared/models.py +206 -0
  321. chunkhound/services/research/shared/query_expander.py +157 -0
  322. chunkhound/services/research/shared/unified_search.py +694 -0
  323. chunkhound/services/research/v1/__init__.py +24 -0
  324. chunkhound/services/research/v1/pluggable_research_service.py +857 -0
  325. chunkhound/services/research/v1/quality_validator.py +257 -0
  326. chunkhound/services/research/v1/question_generator.py +456 -0
  327. chunkhound/services/research/v1/synthesis_engine.py +670 -0
  328. chunkhound/services/search/__init__.py +13 -0
  329. chunkhound/services/search/context_retriever.py +117 -0
  330. chunkhound/services/search/multi_hop_strategy.py +355 -0
  331. chunkhound/services/search/result_enhancer.py +169 -0
  332. chunkhound/services/search/single_hop_strategy.py +82 -0
  333. chunkhound/services/search_service.py +385 -0
  334. chunkhound/tools/__init__.py +31 -0
  335. chunkhound/tools/calibrate_batch_size.py +440 -0
  336. chunkhound/tools/eval/__init__.py +30 -0
  337. chunkhound/tools/eval/language_samples.py +707 -0
  338. chunkhound/tools/eval/metrics.py +235 -0
  339. chunkhound/tools/eval/search.py +520 -0
  340. chunkhound/tools/eval_cluster.py +428 -0
  341. chunkhound/tools/eval_search.py +20 -0
  342. chunkhound/tree_cache.py +350 -0
  343. chunkhound/utils/__init__.py +1 -0
  344. chunkhound/utils/chunk_deduplication.py +284 -0
  345. chunkhound/utils/chunk_hashing.py +77 -0
  346. chunkhound/utils/file_patterns.py +692 -0
  347. chunkhound/utils/git_discovery.py +243 -0
  348. chunkhound/utils/git_safe.py +129 -0
  349. chunkhound/utils/hashing.py +34 -0
  350. chunkhound/utils/ignore_engine.py +710 -0
  351. chunkhound/utils/json_extraction.py +43 -0
  352. chunkhound/utils/metadata.py +98 -0
  353. chunkhound/utils/normalization.py +27 -0
  354. chunkhound/utils/project_detection.py +102 -0
  355. chunkhound/utils/text.py +55 -0
  356. chunkhound/utils/text_sanitization.py +34 -0
  357. chunkhound/utils/tree_formatter.py +170 -0
  358. chunkhound/utils/windows_constants.py +53 -0
  359. chunkhound/version.py +18 -0
  360. chunkhound/watchman/__init__.py +35 -0
  361. chunkhound/watchman/scope.py +349 -0
  362. chunkhound/watchman/session.py +1039 -0
  363. chunkhound/watchman/sidecar.py +724 -0
  364. chunkhound/watchman_runtime/README.md +30 -0
  365. chunkhound/watchman_runtime/__init__.py +45 -0
  366. chunkhound/watchman_runtime/bridge.py +477 -0
  367. chunkhound/watchman_runtime/loader.py +1632 -0
  368. chunkhound/watchman_runtime/platforms/windows-x86_64/bin/eledo-pty-bridge.exe +0 -0
  369. chunkhound/watchman_runtime/platforms/windows-x86_64/bin/gflags.dll +0 -0
  370. chunkhound/watchman_runtime/platforms/windows-x86_64/bin/glog.dll +0 -0
  371. chunkhound/watchman_runtime/platforms/windows-x86_64/bin/libcrypto-3.dll +0 -0
  372. chunkhound/watchman_runtime/platforms/windows-x86_64/bin/watchman-diag.exe +0 -0
  373. chunkhound/watchman_runtime/platforms/windows-x86_64/bin/watchman-make.exe +0 -0
  374. chunkhound/watchman_runtime/platforms/windows-x86_64/bin/watchman-replicate-subscription.exe +0 -0
  375. chunkhound/watchman_runtime/platforms/windows-x86_64/bin/watchman-wait.exe +0 -0
  376. chunkhound/watchman_runtime/platforms/windows-x86_64/bin/watchman.exe +0 -0
  377. chunkhound/watchman_runtime/platforms/windows-x86_64/bin/watchmanctl.exe +0 -0
  378. chunkhound/watchman_runtime/platforms/windows-x86_64/manifest.json +28 -0
  379. chunkhound-5.0.0.dist-info/METADATA +204 -0
  380. chunkhound-5.0.0.dist-info/RECORD +383 -0
  381. chunkhound-5.0.0.dist-info/WHEEL +4 -0
  382. chunkhound-5.0.0.dist-info/entry_points.txt +3 -0
  383. chunkhound-5.0.0.dist-info/licenses/LICENSE +21 -0
chunkhound/__init__.py ADDED
@@ -0,0 +1,27 @@
1
+ """ChunkHound - Local-first semantic code search with vector and regex capabilities."""
2
+ # ci: merge queue test
3
+
4
+ from .version import __version__
5
+
6
+ __author__ = "Ofri Wolfus"
7
+ __description__ = "Local-first semantic code search with vector and regex capabilities"
8
+
9
+ # Import modules only when needed to avoid dependency issues during setup
10
+ __all__ = [
11
+ "Database",
12
+ "CodeParser",
13
+ "__version__",
14
+ ]
15
+
16
+
17
+ def __getattr__(name: str):
18
+ """Lazy import to avoid dependency issues during setup."""
19
+ if name == "Database":
20
+ from .database import Database
21
+
22
+ return Database
23
+ elif name == "CodeParser":
24
+ from .parser import CodeParser
25
+
26
+ return CodeParser
27
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
chunkhound/_version.py ADDED
@@ -0,0 +1,24 @@
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '5.0.0'
22
+ __version_tuple__ = version_tuple = (5, 0, 0)
23
+
24
+ __commit_id__ = commit_id = None
@@ -0,0 +1,5 @@
1
+ """ChunkHound API package - provides CLI and server interfaces."""
2
+
3
+ from chunkhound.version import __version__
4
+
5
+ __all__: list[str] = []
@@ -0,0 +1,9 @@
1
+ """ChunkHound CLI API package - modular command-line interface."""
2
+
3
+ # Removed eager imports to eliminate 958-module import cascade
4
+ # Commands are now imported lazily in main.py when needed
5
+
6
+ __all__ = [
7
+ "run_command",
8
+ "mcp_command",
9
+ ]
@@ -0,0 +1,12 @@
1
+ """ChunkHound CLI commands package - modular command implementations."""
2
+
3
+ # Removed eager imports to eliminate 958-module import cascade
4
+ # Commands are now imported lazily in main.py when needed
5
+
6
+ __all__ = [
7
+ "run_command",
8
+ "mcp_command",
9
+ "search_command",
10
+ "research_command",
11
+ "calibrate_command",
12
+ ]
@@ -0,0 +1,104 @@
1
+ """AutoDoc site generator command module."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ from loguru import logger
10
+
11
+ from chunkhound.api.cli.utils.rich_output import RichOutputFormatter
12
+ from chunkhound.autodoc.docsite import write_astro_assets_only
13
+ from chunkhound.core.config.config import Config
14
+
15
+ from . import autodoc_autorun as autorun
16
+ from . import autodoc_cleanup as cleanup
17
+ from . import autodoc_generate as generate
18
+ from . import autodoc_git as git_utils
19
+ from .autodoc_errors import AutoDocCLIExitError
20
+
21
+
22
+ def _render_exit(formatter: RichOutputFormatter, exc: AutoDocCLIExitError) -> None:
23
+ for message in exc.infos:
24
+ formatter.info(message)
25
+ for message in exc.warnings:
26
+ formatter.warning(message)
27
+ for message in exc.errors:
28
+ formatter.error(message)
29
+
30
+
31
+ async def autodoc_command(args: argparse.Namespace, config: Config) -> None:
32
+ """Generate an Astro docs site from AutoDoc outputs."""
33
+ formatter = RichOutputFormatter(verbose=bool(getattr(args, "verbose", False)))
34
+ output_dir = Path(getattr(args, "out_dir")).resolve()
35
+
36
+ try:
37
+ git_utils.maybe_warn_git_output_dir(output_dir, formatter)
38
+
39
+ if bool(getattr(args, "assets_only", False)):
40
+ if not output_dir.exists():
41
+ raise AutoDocCLIExitError(
42
+ exit_code=1,
43
+ errors=(
44
+ "Output directory not found for --assets-only: "
45
+ f"{output_dir}. Run a full `chunkhound autodoc` first.",
46
+ ),
47
+ )
48
+ write_astro_assets_only(output_dir=output_dir)
49
+ formatter.success("AutoDoc assets update complete.")
50
+ formatter.info(f"Output directory: {output_dir}")
51
+ return
52
+
53
+ map_dir = await autorun.ensure_map_dir(
54
+ args=args,
55
+ config=config,
56
+ formatter=formatter,
57
+ output_dir=output_dir,
58
+ )
59
+
60
+ cleanup_config, llm_manager = cleanup.resolve_cleanup_config_and_llm_manager(
61
+ args=args,
62
+ config=config,
63
+ formatter=formatter,
64
+ )
65
+
66
+ allow_delete_topics_dir = autorun.resolve_allow_delete_topics_dir(
67
+ args=args,
68
+ output_dir=output_dir,
69
+ )
70
+
71
+ inputs = generate.DocsiteGenerationInputs(
72
+ map_dir=map_dir,
73
+ output_dir=output_dir,
74
+ llm_manager=llm_manager,
75
+ cleanup_config=cleanup_config,
76
+ allow_delete_topics_dir=allow_delete_topics_dir,
77
+ index_patterns=getattr(args, "index_patterns", None),
78
+ site_title=getattr(args, "site_title", None),
79
+ site_tagline=getattr(args, "site_tagline", None),
80
+ )
81
+
82
+ result = await generate.generate_docsite_with_optional_autorun(
83
+ args=args,
84
+ config=config,
85
+ formatter=formatter,
86
+ inputs=inputs,
87
+ )
88
+
89
+ except AutoDocCLIExitError as exc:
90
+ _render_exit(formatter, exc)
91
+ sys.exit(exc.exit_code)
92
+ except Exception as exc: # noqa: BLE001
93
+ formatter.error(f"AutoDoc generation failed: {exc}")
94
+ logger.exception("AutoDoc generation failed")
95
+ sys.exit(1)
96
+
97
+ formatter.success("AutoDoc generation complete.")
98
+ formatter.info(f"Output directory: {result.output_dir}")
99
+ formatter.info(f"Pages generated: {len(result.pages)}")
100
+ if result.missing_topics:
101
+ formatter.warning(
102
+ "Missing topic files referenced in index: "
103
+ + ", ".join(result.missing_topics)
104
+ )
@@ -0,0 +1,464 @@
1
+ from __future__ import annotations
2
+
3
+ from argparse import Namespace
4
+ from dataclasses import dataclass
5
+ from pathlib import Path
6
+
7
+ from chunkhound.api.cli.utils.rich_output import RichOutputFormatter
8
+ from chunkhound.core.config.config import Config
9
+
10
+ from . import autodoc_prompts as prompts
11
+ from .autodoc_errors import AutoDocCLIExitError
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class AutoMapPlan:
16
+ map_out_dir: Path
17
+ map_scope: Path
18
+ comprehensiveness: str
19
+ audience: str
20
+
21
+
22
+ @dataclass(frozen=True)
23
+ class AutoMapOptions:
24
+ map_out_dir: Path
25
+ comprehensiveness: str
26
+ audience: str
27
+ map_context: Path | None
28
+
29
+
30
+ def _dedup_preserve_order(items: list[str]) -> list[str]:
31
+ seen: set[str] = set()
32
+ output: list[str] = []
33
+ for item in items:
34
+ if item in seen:
35
+ continue
36
+ seen.add(item)
37
+ output.append(item)
38
+ return output
39
+
40
+
41
+ def _build_auto_map_plan(
42
+ *,
43
+ output_dir: Path,
44
+ map_out_dir: Path | None = None,
45
+ comprehensiveness: str | None = None,
46
+ audience: str | None = None,
47
+ ) -> AutoMapPlan:
48
+ default_map_out_dir = output_dir.with_name(f"map_{output_dir.name}")
49
+ map_scope = Path.cwd().resolve()
50
+ return AutoMapPlan(
51
+ map_out_dir=map_out_dir or default_map_out_dir,
52
+ map_scope=map_scope,
53
+ comprehensiveness=comprehensiveness or "medium",
54
+ audience=audience or "balanced",
55
+ )
56
+
57
+
58
+ def _resolve_map_out_dir(*, args: Namespace, output_dir: Path) -> Path:
59
+ map_out_dir_arg = getattr(args, "map_out_dir", None)
60
+ default_plan = _build_auto_map_plan(output_dir=output_dir)
61
+ map_out_dir_hint = (
62
+ Path(map_out_dir_arg).expanduser()
63
+ if map_out_dir_arg is not None
64
+ else default_plan.map_out_dir
65
+ )
66
+
67
+ map_out_dir = Path(map_out_dir_arg).expanduser() if map_out_dir_arg else None
68
+ if map_out_dir is None:
69
+ raw = prompts.prompt_text(
70
+ "Where should Code Mapper write its outputs",
71
+ default=str(map_out_dir_hint),
72
+ )
73
+ map_out_dir = Path(raw).expanduser() if raw else map_out_dir_hint
74
+ if not map_out_dir.is_absolute():
75
+ map_out_dir = (Path.cwd() / map_out_dir).resolve()
76
+ return map_out_dir
77
+
78
+
79
+ def _resolve_map_comprehensiveness(*, args: Namespace) -> str:
80
+ map_comprehensiveness_arg = getattr(args, "map_comprehensiveness", None)
81
+ comprehensiveness = (
82
+ map_comprehensiveness_arg
83
+ if isinstance(map_comprehensiveness_arg, str)
84
+ else None
85
+ )
86
+ if comprehensiveness is None:
87
+ return prompts.prompt_choice(
88
+ "Code Mapper comprehensiveness",
89
+ choices=("minimal", "low", "medium", "high", "ultra"),
90
+ default="medium",
91
+ )
92
+ return comprehensiveness
93
+
94
+
95
+ def _resolve_map_audience(*, args: Namespace) -> str:
96
+ map_audience_arg = getattr(args, "map_audience", None)
97
+ map_audience = map_audience_arg if isinstance(map_audience_arg, str) else None
98
+ if map_audience is None:
99
+ default_audience = getattr(args, "audience", "balanced")
100
+ return prompts.prompt_choice(
101
+ "Code Mapper audience (map generation)",
102
+ choices=("technical", "balanced", "end-user"),
103
+ default=default_audience,
104
+ )
105
+ return map_audience
106
+
107
+
108
+ def _resolve_map_context(*, args: Namespace) -> Path | None:
109
+ map_context_arg = getattr(args, "map_context", None)
110
+ map_context: Path | None = (
111
+ Path(map_context_arg).expanduser() if map_context_arg is not None else None
112
+ )
113
+ if map_context is None:
114
+ raw = prompts.prompt_text(
115
+ "Optional Code Mapper context file (--map-context, leave blank for none)",
116
+ default=None,
117
+ )
118
+ map_context = Path(raw).expanduser() if raw else None
119
+ return map_context
120
+
121
+
122
+ def resolve_auto_map_options(*, args: Namespace, output_dir: Path) -> AutoMapOptions:
123
+ return AutoMapOptions(
124
+ map_out_dir=_resolve_map_out_dir(args=args, output_dir=output_dir),
125
+ comprehensiveness=_resolve_map_comprehensiveness(args=args),
126
+ audience=_resolve_map_audience(args=args),
127
+ map_context=_resolve_map_context(args=args),
128
+ )
129
+
130
+
131
+ def _effective_config_for_code_mapper_autorun(
132
+ *,
133
+ config: Config,
134
+ config_path: Path | None,
135
+ ) -> Config:
136
+ from chunkhound.api.cli.utils import apply_code_mapper_workspace_overrides
137
+
138
+ effective = config.model_copy(deep=True)
139
+ args = Namespace(
140
+ config=config_path,
141
+ db=None,
142
+ database_path=None,
143
+ )
144
+ apply_code_mapper_workspace_overrides(config=effective, args=args)
145
+ return effective
146
+
147
+
148
+ def _code_mapper_autorun_database_prereqs(
149
+ effective: Config,
150
+ ) -> tuple[list[str], list[str]]:
151
+ missing: list[str] = []
152
+ details: list[str] = []
153
+ try:
154
+ db_path = effective.database.get_db_path()
155
+ except ValueError:
156
+ missing.append("database")
157
+ details.append("- Database path is not configured.")
158
+ return missing, details
159
+
160
+ if not db_path.exists():
161
+ missing.append("database")
162
+ details.append(f"- Database not found at: {db_path}")
163
+
164
+ return missing, details
165
+
166
+
167
+ def _provider_supports_reranking(provider: object) -> bool:
168
+ try:
169
+ supports = getattr(provider, "supports_reranking", None)
170
+ if callable(supports):
171
+ return bool(supports())
172
+ except Exception:
173
+ return False
174
+ return False
175
+
176
+
177
+ def _code_mapper_autorun_embedding_prereqs(
178
+ effective: Config,
179
+ ) -> tuple[list[str], list[str]]:
180
+ from chunkhound.core.config.embedding_factory import EmbeddingProviderFactory
181
+
182
+ missing: list[str] = []
183
+ details: list[str] = []
184
+
185
+ if effective.embedding is None:
186
+ missing.append("embeddings")
187
+ details.append("- Embedding provider is not configured.")
188
+ return missing, details
189
+
190
+ try:
191
+ provider = EmbeddingProviderFactory.create_provider(effective.embedding)
192
+ except (OSError, RuntimeError, TypeError, ValueError) as exc:
193
+ missing.append("embeddings")
194
+ details.append(f"- Embedding provider setup failed: {exc}")
195
+ return missing, details
196
+
197
+ if not _provider_supports_reranking(provider):
198
+ missing.append("reranking")
199
+ details.append(
200
+ "- Embedding provider does not support reranking with current "
201
+ "config (configure reranking; typically `embedding.rerank_model`)."
202
+ )
203
+
204
+ return missing, details
205
+
206
+
207
+ def _code_mapper_autorun_llm_prereqs(effective: Config) -> tuple[list[str], list[str]]:
208
+ missing: list[str] = []
209
+ details: list[str] = []
210
+
211
+ if effective.llm is None:
212
+ missing.append("llm")
213
+ details.append("- LLM provider is not configured.")
214
+ return missing, details
215
+
216
+ if not effective.llm.is_provider_configured():
217
+ missing.append("llm")
218
+ details.append("- LLM provider is not fully configured.")
219
+
220
+ return missing, details
221
+
222
+
223
+ def code_mapper_autorun_prereq_summary(
224
+ *,
225
+ config: Config,
226
+ config_path: Path | None,
227
+ ) -> tuple[bool, list[str], list[str]]:
228
+ """Return (ok, missing_labels, detail_lines) for Code Mapper auto-run."""
229
+ effective = _effective_config_for_code_mapper_autorun(
230
+ config=config,
231
+ config_path=config_path,
232
+ )
233
+
234
+ missing: list[str] = []
235
+ details: list[str] = []
236
+
237
+ db_missing, db_details = _code_mapper_autorun_database_prereqs(effective)
238
+ missing.extend(db_missing)
239
+ details.extend(db_details)
240
+
241
+ embed_missing, embed_details = _code_mapper_autorun_embedding_prereqs(effective)
242
+ missing.extend(embed_missing)
243
+ details.extend(embed_details)
244
+
245
+ llm_missing, llm_details = _code_mapper_autorun_llm_prereqs(effective)
246
+ missing.extend(llm_missing)
247
+ details.extend(llm_details)
248
+
249
+ missing_dedup = _dedup_preserve_order(missing)
250
+ return not missing_dedup, missing_dedup, details
251
+
252
+
253
+ def _autorun_prereq_failure_exit(*, details: list[str], exit_code: int) -> None:
254
+ raise AutoDocCLIExitError(
255
+ exit_code=exit_code,
256
+ errors=(
257
+ "AutoDoc can auto-run Code Mapper, but required prerequisites are missing.",
258
+ *tuple(details),
259
+ ),
260
+ infos=(
261
+ "To fix:",
262
+ "- Run `chunkhound index <directory>` to create the database.",
263
+ (
264
+ "- Configure embeddings with reranking support "
265
+ "(e.g. set `embedding.rerank_model`)."
266
+ ),
267
+ "- Configure an LLM provider (e.g. `CHUNKHOUND_LLM_API_KEY`).",
268
+ ),
269
+ )
270
+
271
+
272
+ def confirm_autorun_and_validate_prereqs(
273
+ *,
274
+ config: Config,
275
+ config_path: Path | None,
276
+ question: str,
277
+ decline_error: str,
278
+ decline_exit_code: int,
279
+ prereq_failure_exit_code: int = 1,
280
+ default: bool = False,
281
+ ) -> None:
282
+ preflight_ok, missing, _details = code_mapper_autorun_prereq_summary(
283
+ config=config,
284
+ config_path=config_path,
285
+ )
286
+ warning_suffix = ""
287
+ if not preflight_ok:
288
+ warning_suffix = (
289
+ "\n\n"
290
+ "Note: Code Mapper prerequisites appear missing "
291
+ f"({', '.join(missing)})."
292
+ )
293
+
294
+ if not prompts.prompt_yes_no(
295
+ f"{question}{warning_suffix}",
296
+ default=default,
297
+ ):
298
+ raise AutoDocCLIExitError(exit_code=decline_exit_code, errors=(decline_error,))
299
+
300
+ preflight_ok, _missing, details = code_mapper_autorun_prereq_summary(
301
+ config=config,
302
+ config_path=config_path,
303
+ )
304
+ if not preflight_ok:
305
+ _autorun_prereq_failure_exit(
306
+ details=details, exit_code=prereq_failure_exit_code
307
+ )
308
+
309
+
310
+ async def run_code_mapper_for_autodoc(
311
+ *,
312
+ config: Config,
313
+ formatter: RichOutputFormatter,
314
+ output_dir: Path,
315
+ verbose: bool,
316
+ config_path: Path | None,
317
+ map_out_dir: Path | None,
318
+ map_context: Path | None,
319
+ comprehensiveness: str | None,
320
+ audience: str | None,
321
+ ) -> AutoMapPlan:
322
+ from chunkhound.api.cli.commands.code_mapper import code_mapper_command
323
+
324
+ plan = _build_auto_map_plan(
325
+ output_dir=output_dir,
326
+ map_out_dir=map_out_dir,
327
+ comprehensiveness=comprehensiveness,
328
+ audience=audience,
329
+ )
330
+
331
+ map_args = Namespace(
332
+ command="map",
333
+ verbose=verbose,
334
+ debug=False,
335
+ config=config_path,
336
+ path=plan.map_scope,
337
+ out=plan.map_out_dir,
338
+ context=map_context,
339
+ overview_only=False,
340
+ comprehensiveness=plan.comprehensiveness,
341
+ combined=False,
342
+ audience=plan.audience,
343
+ )
344
+
345
+ try:
346
+ await code_mapper_command(map_args, config)
347
+ except SystemExit as exc:
348
+ code = exc.code if isinstance(exc.code, int) else 1
349
+ raise AutoDocCLIExitError(
350
+ exit_code=code,
351
+ errors=("Map generation failed; aborting AutoDoc.",),
352
+ )
353
+
354
+ return plan
355
+
356
+
357
+ async def autorun_code_mapper_for_autodoc(
358
+ *,
359
+ args: Namespace,
360
+ config: Config,
361
+ formatter: RichOutputFormatter,
362
+ output_dir: Path,
363
+ question: str,
364
+ decline_error: str,
365
+ decline_exit_code: int,
366
+ ) -> Path:
367
+ config_path = getattr(args, "config", None)
368
+ confirm_autorun_and_validate_prereqs(
369
+ config=config,
370
+ config_path=config_path,
371
+ question=question,
372
+ decline_error=decline_error,
373
+ decline_exit_code=decline_exit_code,
374
+ )
375
+
376
+ map_options = resolve_auto_map_options(args=args, output_dir=output_dir)
377
+ formatter.info(f"Generating maps via Code Mapper: {map_options.map_out_dir}")
378
+ plan = await run_code_mapper_for_autodoc(
379
+ config=config,
380
+ formatter=formatter,
381
+ output_dir=output_dir,
382
+ verbose=bool(getattr(args, "verbose", False)),
383
+ config_path=config_path,
384
+ map_out_dir=map_options.map_out_dir,
385
+ map_context=map_options.map_context,
386
+ comprehensiveness=map_options.comprehensiveness,
387
+ audience=map_options.audience,
388
+ )
389
+ return plan.map_out_dir
390
+
391
+
392
+ async def ensure_map_dir(
393
+ *,
394
+ args: Namespace,
395
+ config: Config,
396
+ formatter: RichOutputFormatter,
397
+ output_dir: Path,
398
+ ) -> Path:
399
+ map_in_arg = getattr(args, "map_in", None)
400
+ if map_in_arg is None:
401
+ if not prompts.is_interactive():
402
+ raise AutoDocCLIExitError(
403
+ exit_code=2,
404
+ errors=(
405
+ "Missing required input: map-in (Code Mapper outputs directory). "
406
+ "Non-interactive mode cannot prompt to auto-generate maps.",
407
+ ),
408
+ )
409
+
410
+ return await autorun_code_mapper_for_autodoc(
411
+ args=args,
412
+ config=config,
413
+ formatter=formatter,
414
+ output_dir=output_dir,
415
+ question=(
416
+ "No `map-in` provided. Generate the codemap first by running "
417
+ "`chunkhound map`, then continue with AutoDoc?"
418
+ ),
419
+ decline_error=(
420
+ "Missing required input: map-in (Code Mapper outputs directory)."
421
+ ),
422
+ decline_exit_code=2,
423
+ )
424
+
425
+ map_dir = Path(map_in_arg).resolve()
426
+ if not map_dir.exists():
427
+ raise AutoDocCLIExitError(
428
+ exit_code=1,
429
+ errors=(f"Map outputs directory not found: {map_dir}",),
430
+ )
431
+ return map_dir
432
+
433
+
434
+ def resolve_allow_delete_topics_dir(
435
+ *,
436
+ args: Namespace,
437
+ output_dir: Path,
438
+ ) -> bool:
439
+ topics_dir = output_dir / "src" / "pages" / "topics"
440
+ if not topics_dir.exists():
441
+ return False
442
+
443
+ force = bool(getattr(args, "force", False))
444
+ if force:
445
+ return True
446
+
447
+ if not prompts.is_interactive():
448
+ raise AutoDocCLIExitError(
449
+ exit_code=2,
450
+ errors=(
451
+ "Output directory already contains topic pages at "
452
+ f"{topics_dir}. Re-generating will delete them. "
453
+ "Re-run with `--force` to allow deletion.",
454
+ ),
455
+ )
456
+
457
+ if not prompts.prompt_yes_no(
458
+ "Output directory already contains generated topic pages at "
459
+ f"{topics_dir}. Delete and re-generate them?",
460
+ default=False,
461
+ ):
462
+ raise AutoDocCLIExitError(exit_code=2, errors=("Aborted.",))
463
+
464
+ return True