crackerjack 0.33.0__py3-none-any.whl → 0.33.2__py3-none-any.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.

Potentially problematic release.


This version of crackerjack might be problematic. Click here for more details.

Files changed (198) hide show
  1. crackerjack/__main__.py +1350 -34
  2. crackerjack/adapters/__init__.py +17 -0
  3. crackerjack/adapters/lsp_client.py +358 -0
  4. crackerjack/adapters/rust_tool_adapter.py +194 -0
  5. crackerjack/adapters/rust_tool_manager.py +193 -0
  6. crackerjack/adapters/skylos_adapter.py +231 -0
  7. crackerjack/adapters/zuban_adapter.py +560 -0
  8. crackerjack/agents/base.py +7 -3
  9. crackerjack/agents/coordinator.py +271 -33
  10. crackerjack/agents/documentation_agent.py +9 -15
  11. crackerjack/agents/dry_agent.py +3 -15
  12. crackerjack/agents/formatting_agent.py +1 -1
  13. crackerjack/agents/import_optimization_agent.py +36 -180
  14. crackerjack/agents/performance_agent.py +17 -98
  15. crackerjack/agents/performance_helpers.py +7 -31
  16. crackerjack/agents/proactive_agent.py +1 -3
  17. crackerjack/agents/refactoring_agent.py +16 -85
  18. crackerjack/agents/refactoring_helpers.py +7 -42
  19. crackerjack/agents/security_agent.py +9 -48
  20. crackerjack/agents/test_creation_agent.py +356 -513
  21. crackerjack/agents/test_specialist_agent.py +0 -4
  22. crackerjack/api.py +6 -25
  23. crackerjack/cli/cache_handlers.py +204 -0
  24. crackerjack/cli/cache_handlers_enhanced.py +683 -0
  25. crackerjack/cli/facade.py +100 -0
  26. crackerjack/cli/handlers.py +224 -9
  27. crackerjack/cli/interactive.py +6 -4
  28. crackerjack/cli/options.py +642 -55
  29. crackerjack/cli/utils.py +2 -1
  30. crackerjack/code_cleaner.py +58 -117
  31. crackerjack/config/global_lock_config.py +8 -48
  32. crackerjack/config/hooks.py +53 -62
  33. crackerjack/core/async_workflow_orchestrator.py +24 -34
  34. crackerjack/core/autofix_coordinator.py +3 -17
  35. crackerjack/core/enhanced_container.py +4 -13
  36. crackerjack/core/file_lifecycle.py +12 -89
  37. crackerjack/core/performance.py +2 -2
  38. crackerjack/core/performance_monitor.py +15 -55
  39. crackerjack/core/phase_coordinator.py +104 -204
  40. crackerjack/core/resource_manager.py +14 -90
  41. crackerjack/core/service_watchdog.py +62 -95
  42. crackerjack/core/session_coordinator.py +149 -0
  43. crackerjack/core/timeout_manager.py +14 -72
  44. crackerjack/core/websocket_lifecycle.py +13 -78
  45. crackerjack/core/workflow_orchestrator.py +171 -174
  46. crackerjack/docs/INDEX.md +11 -0
  47. crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
  48. crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
  49. crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
  50. crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
  51. crackerjack/docs/generated/api/SERVICES.md +1252 -0
  52. crackerjack/documentation/__init__.py +31 -0
  53. crackerjack/documentation/ai_templates.py +756 -0
  54. crackerjack/documentation/dual_output_generator.py +765 -0
  55. crackerjack/documentation/mkdocs_integration.py +518 -0
  56. crackerjack/documentation/reference_generator.py +977 -0
  57. crackerjack/dynamic_config.py +55 -50
  58. crackerjack/executors/async_hook_executor.py +10 -15
  59. crackerjack/executors/cached_hook_executor.py +117 -43
  60. crackerjack/executors/hook_executor.py +8 -34
  61. crackerjack/executors/hook_lock_manager.py +26 -183
  62. crackerjack/executors/individual_hook_executor.py +13 -11
  63. crackerjack/executors/lsp_aware_hook_executor.py +270 -0
  64. crackerjack/executors/tool_proxy.py +417 -0
  65. crackerjack/hooks/lsp_hook.py +79 -0
  66. crackerjack/intelligence/adaptive_learning.py +25 -10
  67. crackerjack/intelligence/agent_orchestrator.py +2 -5
  68. crackerjack/intelligence/agent_registry.py +34 -24
  69. crackerjack/intelligence/agent_selector.py +5 -7
  70. crackerjack/interactive.py +17 -6
  71. crackerjack/managers/async_hook_manager.py +0 -1
  72. crackerjack/managers/hook_manager.py +79 -1
  73. crackerjack/managers/publish_manager.py +44 -8
  74. crackerjack/managers/test_command_builder.py +1 -15
  75. crackerjack/managers/test_executor.py +1 -3
  76. crackerjack/managers/test_manager.py +98 -7
  77. crackerjack/managers/test_manager_backup.py +10 -9
  78. crackerjack/mcp/cache.py +2 -2
  79. crackerjack/mcp/client_runner.py +1 -1
  80. crackerjack/mcp/context.py +191 -68
  81. crackerjack/mcp/dashboard.py +7 -5
  82. crackerjack/mcp/enhanced_progress_monitor.py +31 -28
  83. crackerjack/mcp/file_monitor.py +30 -23
  84. crackerjack/mcp/progress_components.py +31 -21
  85. crackerjack/mcp/progress_monitor.py +50 -53
  86. crackerjack/mcp/rate_limiter.py +6 -6
  87. crackerjack/mcp/server_core.py +17 -16
  88. crackerjack/mcp/service_watchdog.py +2 -1
  89. crackerjack/mcp/state.py +4 -7
  90. crackerjack/mcp/task_manager.py +11 -9
  91. crackerjack/mcp/tools/core_tools.py +173 -32
  92. crackerjack/mcp/tools/error_analyzer.py +3 -2
  93. crackerjack/mcp/tools/execution_tools.py +8 -10
  94. crackerjack/mcp/tools/execution_tools_backup.py +42 -30
  95. crackerjack/mcp/tools/intelligence_tool_registry.py +7 -5
  96. crackerjack/mcp/tools/intelligence_tools.py +5 -2
  97. crackerjack/mcp/tools/monitoring_tools.py +33 -70
  98. crackerjack/mcp/tools/proactive_tools.py +24 -11
  99. crackerjack/mcp/tools/progress_tools.py +5 -8
  100. crackerjack/mcp/tools/utility_tools.py +20 -14
  101. crackerjack/mcp/tools/workflow_executor.py +62 -40
  102. crackerjack/mcp/websocket/app.py +8 -0
  103. crackerjack/mcp/websocket/endpoints.py +352 -357
  104. crackerjack/mcp/websocket/jobs.py +40 -57
  105. crackerjack/mcp/websocket/monitoring_endpoints.py +2935 -0
  106. crackerjack/mcp/websocket/server.py +7 -25
  107. crackerjack/mcp/websocket/websocket_handler.py +6 -17
  108. crackerjack/mixins/__init__.py +0 -2
  109. crackerjack/mixins/error_handling.py +1 -70
  110. crackerjack/models/config.py +12 -1
  111. crackerjack/models/config_adapter.py +49 -1
  112. crackerjack/models/protocols.py +122 -122
  113. crackerjack/models/resource_protocols.py +55 -210
  114. crackerjack/monitoring/ai_agent_watchdog.py +13 -13
  115. crackerjack/monitoring/metrics_collector.py +426 -0
  116. crackerjack/monitoring/regression_prevention.py +8 -8
  117. crackerjack/monitoring/websocket_server.py +643 -0
  118. crackerjack/orchestration/advanced_orchestrator.py +11 -6
  119. crackerjack/orchestration/coverage_improvement.py +3 -3
  120. crackerjack/orchestration/execution_strategies.py +26 -6
  121. crackerjack/orchestration/test_progress_streamer.py +8 -5
  122. crackerjack/plugins/base.py +2 -2
  123. crackerjack/plugins/hooks.py +7 -0
  124. crackerjack/plugins/managers.py +11 -8
  125. crackerjack/security/__init__.py +0 -1
  126. crackerjack/security/audit.py +6 -35
  127. crackerjack/services/anomaly_detector.py +392 -0
  128. crackerjack/services/api_extractor.py +615 -0
  129. crackerjack/services/backup_service.py +2 -2
  130. crackerjack/services/bounded_status_operations.py +15 -152
  131. crackerjack/services/cache.py +127 -1
  132. crackerjack/services/changelog_automation.py +395 -0
  133. crackerjack/services/config.py +15 -9
  134. crackerjack/services/config_merge.py +19 -80
  135. crackerjack/services/config_template.py +506 -0
  136. crackerjack/services/contextual_ai_assistant.py +48 -22
  137. crackerjack/services/coverage_badge_service.py +171 -0
  138. crackerjack/services/coverage_ratchet.py +27 -25
  139. crackerjack/services/debug.py +3 -3
  140. crackerjack/services/dependency_analyzer.py +460 -0
  141. crackerjack/services/dependency_monitor.py +14 -11
  142. crackerjack/services/documentation_generator.py +491 -0
  143. crackerjack/services/documentation_service.py +675 -0
  144. crackerjack/services/enhanced_filesystem.py +6 -5
  145. crackerjack/services/enterprise_optimizer.py +865 -0
  146. crackerjack/services/error_pattern_analyzer.py +676 -0
  147. crackerjack/services/file_hasher.py +1 -1
  148. crackerjack/services/git.py +8 -25
  149. crackerjack/services/health_metrics.py +10 -8
  150. crackerjack/services/heatmap_generator.py +735 -0
  151. crackerjack/services/initialization.py +11 -30
  152. crackerjack/services/input_validator.py +5 -97
  153. crackerjack/services/intelligent_commit.py +327 -0
  154. crackerjack/services/log_manager.py +15 -12
  155. crackerjack/services/logging.py +4 -3
  156. crackerjack/services/lsp_client.py +628 -0
  157. crackerjack/services/memory_optimizer.py +19 -87
  158. crackerjack/services/metrics.py +42 -33
  159. crackerjack/services/parallel_executor.py +9 -67
  160. crackerjack/services/pattern_cache.py +1 -1
  161. crackerjack/services/pattern_detector.py +6 -6
  162. crackerjack/services/performance_benchmarks.py +18 -59
  163. crackerjack/services/performance_cache.py +20 -81
  164. crackerjack/services/performance_monitor.py +27 -95
  165. crackerjack/services/predictive_analytics.py +510 -0
  166. crackerjack/services/quality_baseline.py +234 -0
  167. crackerjack/services/quality_baseline_enhanced.py +646 -0
  168. crackerjack/services/quality_intelligence.py +785 -0
  169. crackerjack/services/regex_patterns.py +618 -524
  170. crackerjack/services/regex_utils.py +43 -123
  171. crackerjack/services/secure_path_utils.py +5 -164
  172. crackerjack/services/secure_status_formatter.py +30 -141
  173. crackerjack/services/secure_subprocess.py +11 -92
  174. crackerjack/services/security.py +9 -41
  175. crackerjack/services/security_logger.py +12 -24
  176. crackerjack/services/server_manager.py +124 -16
  177. crackerjack/services/status_authentication.py +16 -159
  178. crackerjack/services/status_security_manager.py +4 -131
  179. crackerjack/services/thread_safe_status_collector.py +19 -125
  180. crackerjack/services/unified_config.py +21 -13
  181. crackerjack/services/validation_rate_limiter.py +5 -54
  182. crackerjack/services/version_analyzer.py +459 -0
  183. crackerjack/services/version_checker.py +1 -1
  184. crackerjack/services/websocket_resource_limiter.py +10 -144
  185. crackerjack/services/zuban_lsp_service.py +390 -0
  186. crackerjack/slash_commands/__init__.py +2 -7
  187. crackerjack/slash_commands/run.md +2 -2
  188. crackerjack/tools/validate_input_validator_patterns.py +14 -40
  189. crackerjack/tools/validate_regex_patterns.py +19 -48
  190. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/METADATA +196 -25
  191. crackerjack-0.33.2.dist-info/RECORD +229 -0
  192. crackerjack/CLAUDE.md +0 -207
  193. crackerjack/RULES.md +0 -380
  194. crackerjack/py313.py +0 -234
  195. crackerjack-0.33.0.dist-info/RECORD +0 -187
  196. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/WHEEL +0 -0
  197. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/entry_points.txt +0 -0
  198. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,675 @@
1
+ """Main documentation service for automated API documentation generation."""
2
+
3
+ import typing as t
4
+ from pathlib import Path
5
+
6
+ from rich.console import Console
7
+
8
+ from ..models.protocols import (
9
+ APIExtractorProtocol,
10
+ DocumentationGeneratorProtocol,
11
+ DocumentationServiceProtocol,
12
+ )
13
+ from .api_extractor import APIExtractorImpl
14
+ from .documentation_generator import DocumentationGeneratorImpl
15
+
16
+
17
+ class DocumentationServiceImpl(DocumentationServiceProtocol):
18
+ """Main service for automated documentation generation and maintenance."""
19
+
20
+ def __init__(
21
+ self,
22
+ console: Console,
23
+ pkg_path: Path,
24
+ api_extractor: APIExtractorProtocol | None = None,
25
+ doc_generator: DocumentationGeneratorProtocol | None = None,
26
+ ) -> None:
27
+ self.console = console
28
+ self.pkg_path = pkg_path
29
+ self.api_extractor = api_extractor or APIExtractorImpl(console)
30
+ self.doc_generator = doc_generator or DocumentationGeneratorImpl(console)
31
+
32
+ # Define standard paths
33
+ self.docs_dir = pkg_path / "docs"
34
+ self.generated_docs_dir = self.docs_dir / "generated"
35
+ self.api_docs_dir = self.generated_docs_dir / "api"
36
+ self.guides_dir = self.generated_docs_dir / "guides"
37
+
38
+ # Ensure directories exist
39
+ self._ensure_directories()
40
+
41
+ def extract_api_documentation(self, source_paths: list[Path]) -> dict[str, t.Any]:
42
+ """Extract API documentation from source code files."""
43
+ self.console.print(
44
+ "[cyan]📖[/cyan] Extracting API documentation from source files..."
45
+ )
46
+
47
+ # Categorize source files
48
+ python_files = [p for p in source_paths if p.suffix == ".py"]
49
+ protocol_files = [p for p in python_files if p.name == "protocols.py"]
50
+ service_files = [p for p in python_files if "/services/" in str(p)]
51
+ manager_files = [p for p in python_files if "/managers/" in str(p)]
52
+ cli_files = [p for p in python_files if "/cli/" in str(p)]
53
+ mcp_files = [
54
+ p for p in source_paths if "/mcp/" in str(p) or p.suffix in (".py", ".md")
55
+ ]
56
+
57
+ api_data = {}
58
+
59
+ # Extract from Python files
60
+ if python_files:
61
+ python_data = self.api_extractor.extract_from_python_files(python_files)
62
+ api_data.update(python_data)
63
+
64
+ # Extract protocol definitions
65
+ if protocol_files:
66
+ for protocol_file in protocol_files:
67
+ protocol_data = self.api_extractor.extract_protocol_definitions(
68
+ protocol_file
69
+ )
70
+ api_data.update(protocol_data)
71
+
72
+ # Extract service interfaces
73
+ if service_files:
74
+ service_data = self.api_extractor.extract_service_interfaces(service_files)
75
+ api_data.update(service_data)
76
+
77
+ # Extract manager interfaces
78
+ if manager_files:
79
+ manager_data = self.api_extractor.extract_service_interfaces(manager_files)
80
+ if "services" in manager_data:
81
+ api_data["managers"] = manager_data["services"]
82
+
83
+ # Extract CLI commands
84
+ if cli_files:
85
+ cli_data = self.api_extractor.extract_cli_commands(cli_files)
86
+ api_data.update(cli_data)
87
+
88
+ # Extract MCP tools
89
+ if mcp_files:
90
+ mcp_data = self.api_extractor.extract_mcp_tools(mcp_files)
91
+ api_data.update(mcp_data)
92
+
93
+ self.console.print(
94
+ f"[green]✅[/green] Extracted documentation from {len(source_paths)} files"
95
+ )
96
+ return api_data
97
+
98
+ def generate_documentation(
99
+ self, template_name: str, context: dict[str, t.Any]
100
+ ) -> str:
101
+ """Generate documentation using specified template."""
102
+ try:
103
+ return self.doc_generator.render_template(Path(template_name), context)
104
+ except (FileNotFoundError, ValueError):
105
+ # Try built-in templates
106
+ return self.doc_generator.generate_api_reference(context)
107
+
108
+ def validate_documentation(self, doc_paths: list[Path]) -> list[dict[str, str]]:
109
+ """Validate documentation for issues and inconsistencies."""
110
+ issues = []
111
+
112
+ for doc_path in doc_paths:
113
+ if not doc_path.exists():
114
+ issues.append(
115
+ {
116
+ "type": "missing_file",
117
+ "path": str(doc_path),
118
+ "message": "Documentation file does not exist",
119
+ }
120
+ )
121
+ continue
122
+
123
+ try:
124
+ content = doc_path.read_text(encoding="utf-8")
125
+
126
+ # Check for broken internal links
127
+ broken_links = self._check_internal_links(content, doc_path)
128
+ issues.extend(broken_links)
129
+
130
+ # Check for empty sections
131
+ empty_sections = self._check_empty_sections(content, doc_path)
132
+ issues.extend(empty_sections)
133
+
134
+ # Check for outdated version references
135
+ outdated_refs = self._check_version_references(content, doc_path)
136
+ issues.extend(outdated_refs)
137
+
138
+ except Exception as e:
139
+ issues.append(
140
+ {
141
+ "type": "read_error",
142
+ "path": str(doc_path),
143
+ "message": f"Could not read file: {e}",
144
+ }
145
+ )
146
+
147
+ return issues
148
+
149
+ def update_documentation_index(self) -> bool:
150
+ """Update the main documentation index with links to all docs."""
151
+ try:
152
+ index_path = self.docs_dir / "INDEX.md"
153
+
154
+ # Collect all documentation files
155
+ api_docs = (
156
+ list[t.Any](self.api_docs_dir.glob("*.md"))
157
+ if self.api_docs_dir.exists()
158
+ else []
159
+ )
160
+ guide_docs = (
161
+ list[t.Any](self.guides_dir.glob("*.md"))
162
+ if self.guides_dir.exists()
163
+ else []
164
+ )
165
+ root_docs = [
166
+ f
167
+ for f in self.docs_dir.glob("*.md")
168
+ if f.name not in ("INDEX.md", "README.md")
169
+ ]
170
+
171
+ # Generate index content
172
+ index_content = self._generate_index_content(
173
+ api_docs, guide_docs, root_docs
174
+ )
175
+
176
+ # Write index file
177
+ index_path.write_text(index_content, encoding="utf-8")
178
+
179
+ self.console.print(
180
+ f"[green]✅[/green] Updated documentation index at {index_path}"
181
+ )
182
+ return True
183
+
184
+ except Exception as e:
185
+ self.console.print(
186
+ f"[red]❌[/red] Failed to update documentation index: {e}"
187
+ )
188
+ return False
189
+
190
+ def get_documentation_coverage(self) -> dict[str, t.Any]:
191
+ """Calculate documentation coverage metrics."""
192
+ # Find all source files
193
+ source_files = self._find_source_files()
194
+
195
+ # Extract API data
196
+ api_data = self.extract_api_documentation(source_files)
197
+
198
+ # Count documented vs undocumented items
199
+ total_items, documented_items = self._count_documentation_items(api_data)
200
+
201
+ # Calculate coverage percentage
202
+ coverage_percentage = (
203
+ (documented_items / total_items * 100) if total_items > 0 else 0.0
204
+ )
205
+
206
+ return {
207
+ "total_items": total_items,
208
+ "documented_items": documented_items,
209
+ "coverage_percentage": coverage_percentage,
210
+ "undocumented_items": total_items - documented_items,
211
+ }
212
+
213
+ def _find_source_files(self) -> list[Path]:
214
+ """Find all Python source files excluding hidden directories."""
215
+ source_files = list[t.Any](self.pkg_path.glob("**/*.py"))
216
+ return [
217
+ f for f in source_files if not any(part.startswith(".") for part in f.parts)
218
+ ]
219
+
220
+ def _count_documentation_items(self, api_data: dict[str, t.Any]) -> tuple[int, int]:
221
+ """Count total and documented items in API data."""
222
+ total_items = 0
223
+ documented_items = 0
224
+
225
+ # Count protocols
226
+ protocol_total, protocol_documented = self._count_protocol_items(
227
+ api_data.get("protocols", {})
228
+ )
229
+ total_items += protocol_total
230
+ documented_items += protocol_documented
231
+
232
+ # Count modules (classes and functions)
233
+ module_total, module_documented = self._count_module_items(
234
+ api_data.get("modules", {})
235
+ )
236
+ total_items += module_total
237
+ documented_items += module_documented
238
+
239
+ return total_items, documented_items
240
+
241
+ def _count_protocol_items(self, protocols: dict[str, t.Any]) -> tuple[int, int]:
242
+ """Count protocol-related documentation items."""
243
+ total_items = 0
244
+ documented_items = 0
245
+
246
+ for protocol_info in protocols.values():
247
+ # Count protocol itself
248
+ total_items += 1
249
+ if protocol_info.get("docstring", {}).get("description"):
250
+ documented_items += 1
251
+
252
+ # Count protocol methods
253
+ method_total, method_documented = self._count_method_items(
254
+ protocol_info.get("methods", [])
255
+ )
256
+ total_items += method_total
257
+ documented_items += method_documented
258
+
259
+ return total_items, documented_items
260
+
261
+ def _count_module_items(self, modules: dict[str, t.Any]) -> tuple[int, int]:
262
+ """Count module-related documentation items."""
263
+ total_items = 0
264
+ documented_items = 0
265
+
266
+ for module_data in modules.values():
267
+ # Count classes
268
+ class_total, class_documented = self._count_class_items(
269
+ module_data.get("classes", [])
270
+ )
271
+ total_items += class_total
272
+ documented_items += class_documented
273
+
274
+ # Count functions
275
+ func_total, func_documented = self._count_function_items(
276
+ module_data.get("functions", [])
277
+ )
278
+ total_items += func_total
279
+ documented_items += func_documented
280
+
281
+ return total_items, documented_items
282
+
283
+ def _count_class_items(self, classes: list[dict[str, t.Any]]) -> tuple[int, int]:
284
+ """Count class-related documentation items."""
285
+ total_items = 0
286
+ documented_items = 0
287
+
288
+ for class_info in classes:
289
+ # Count class itself
290
+ total_items += 1
291
+ if class_info.get("docstring", {}).get("description"):
292
+ documented_items += 1
293
+
294
+ # Count class methods
295
+ method_total, method_documented = self._count_method_items(
296
+ class_info.get("methods", [])
297
+ )
298
+ total_items += method_total
299
+ documented_items += method_documented
300
+
301
+ return total_items, documented_items
302
+
303
+ def _count_function_items(
304
+ self, functions: list[dict[str, t.Any]]
305
+ ) -> tuple[int, int]:
306
+ """Count function documentation items."""
307
+ total_items = len(functions)
308
+ documented_items = sum(
309
+ 1
310
+ for func_info in functions
311
+ if func_info.get("docstring", {}).get("description")
312
+ )
313
+ return total_items, documented_items
314
+
315
+ def _count_method_items(self, methods: list[dict[str, t.Any]]) -> tuple[int, int]:
316
+ """Count method documentation items."""
317
+ total_items = len(methods)
318
+ documented_items = sum(
319
+ 1 for method in methods if method.get("docstring", {}).get("description")
320
+ )
321
+ return total_items, documented_items
322
+
323
+ def generate_full_api_documentation(self) -> bool:
324
+ """Generate complete API documentation for the project."""
325
+ try:
326
+ self.console.print(
327
+ "[cyan]📚[/cyan] Generating complete API documentation..."
328
+ )
329
+
330
+ # Find all source files
331
+ source_files = list[t.Any](self.pkg_path.glob("**/*.py"))
332
+ source_files = [
333
+ f
334
+ for f in source_files
335
+ if not any(part.startswith(".") for part in f.parts)
336
+ ]
337
+
338
+ # Extract API data
339
+ api_data = self.extract_api_documentation(source_files)
340
+
341
+ # Generate API reference
342
+ api_reference = self.doc_generator.generate_api_reference(api_data)
343
+ api_ref_path = self.api_docs_dir / "API_REFERENCE.md"
344
+ api_ref_path.write_text(api_reference, encoding="utf-8")
345
+
346
+ # Generate protocol documentation
347
+ if "protocols" in api_data:
348
+ protocol_docs = self._generate_protocol_documentation(
349
+ api_data["protocols"]
350
+ )
351
+ protocol_path = self.api_docs_dir / "PROTOCOLS.md"
352
+ protocol_path.write_text(protocol_docs, encoding="utf-8")
353
+
354
+ # Generate service documentation
355
+ if "services" in api_data:
356
+ service_docs = self._generate_service_documentation(
357
+ api_data["services"]
358
+ )
359
+ service_path = self.api_docs_dir / "SERVICES.md"
360
+ service_path.write_text(service_docs, encoding="utf-8")
361
+
362
+ # Generate CLI documentation
363
+ if "commands" in api_data:
364
+ cli_docs = self._generate_cli_documentation(api_data["commands"])
365
+ cli_path = self.api_docs_dir / "CLI_REFERENCE.md"
366
+ cli_path.write_text(cli_docs, encoding="utf-8")
367
+
368
+ # Update cross-references
369
+ cross_refs = self.doc_generator.generate_cross_references(api_data)
370
+ if cross_refs:
371
+ cross_ref_path = self.api_docs_dir / "CROSS_REFERENCES.md"
372
+ cross_ref_content = self._format_cross_references(cross_refs)
373
+ cross_ref_path.write_text(cross_ref_content, encoding="utf-8")
374
+
375
+ # Update documentation index
376
+ self.update_documentation_index()
377
+
378
+ self.console.print(
379
+ "[green]🎉[/green] API documentation generation completed!"
380
+ )
381
+ return True
382
+
383
+ except Exception as e:
384
+ self.console.print(
385
+ f"[red]❌[/red] Failed to generate API documentation: {e}"
386
+ )
387
+ return False
388
+
389
+ def _ensure_directories(self) -> None:
390
+ """Ensure all necessary documentation directories exist."""
391
+ directories = [
392
+ self.docs_dir,
393
+ self.generated_docs_dir,
394
+ self.api_docs_dir,
395
+ self.guides_dir,
396
+ ]
397
+
398
+ for directory in directories:
399
+ directory.mkdir(parents=True, exist_ok=True)
400
+
401
+ def _check_internal_links(
402
+ self, content: str, doc_path: Path
403
+ ) -> list[dict[str, str]]:
404
+ """Check for broken internal links in documentation."""
405
+ from .regex_patterns import SAFE_PATTERNS
406
+
407
+ issues = []
408
+
409
+ # Find markdown links [text](path)
410
+ link_pattern = SAFE_PATTERNS["extract_markdown_links"]._get_compiled_pattern()
411
+ matches = link_pattern.findall(content)
412
+
413
+ for link_text, link_path in matches:
414
+ if link_path.startswith(("http://", "https://", "mailto:")):
415
+ continue # Skip external links
416
+
417
+ # Resolve relative path
418
+ if link_path.startswith("/"):
419
+ target_path = self.pkg_path / link_path.lstrip("/")
420
+ else:
421
+ target_path = doc_path.parent / link_path
422
+
423
+ if not target_path.exists():
424
+ issues.append(
425
+ {
426
+ "type": "broken_link",
427
+ "path": str(doc_path),
428
+ "message": f"Broken internal link: [{link_text}]({link_path})",
429
+ }
430
+ )
431
+
432
+ return issues
433
+
434
+ def _check_empty_sections(
435
+ self, content: str, doc_path: Path
436
+ ) -> list[dict[str, str]]:
437
+ """Check for empty sections in documentation."""
438
+ import re
439
+
440
+ issues = []
441
+
442
+ # Find headers followed immediately by another header (empty section)
443
+ empty_section_pattern = re.compile( # REGEX OK: markdown section parsing
444
+ r"(#{1,6}\s+[^\n]+)\n\s*(#{1,6}\s+[^\n]+)", re.MULTILINE
445
+ )
446
+ matches = empty_section_pattern.findall(content)
447
+
448
+ for header1, header2 in matches:
449
+ issues.append(
450
+ {
451
+ "type": "empty_section",
452
+ "path": str(doc_path),
453
+ "message": f"Empty section found: {header1.strip()}",
454
+ }
455
+ )
456
+
457
+ return issues
458
+
459
+ def _check_version_references(
460
+ self, content: str, doc_path: Path
461
+ ) -> list[dict[str, str]]:
462
+ """Check for outdated version references."""
463
+ from .regex_patterns import SAFE_PATTERNS
464
+
465
+ issues = []
466
+
467
+ # Look for version patterns
468
+ version_pattern = SAFE_PATTERNS[
469
+ "extract_version_numbers"
470
+ ]._get_compiled_pattern()
471
+ matches = version_pattern.findall(content)
472
+
473
+ # This is a placeholder - in a real implementation you'd compare with current version
474
+ for version in matches:
475
+ if version != "1.0.0": # Placeholder version check
476
+ issues.append(
477
+ {
478
+ "type": "outdated_version",
479
+ "path": str(doc_path),
480
+ "message": f"Potentially outdated version reference: {version}",
481
+ }
482
+ )
483
+
484
+ return issues
485
+
486
+ def _generate_index_content(
487
+ self, api_docs: list[Path], guide_docs: list[Path], root_docs: list[Path]
488
+ ) -> str:
489
+ """Generate content for the documentation index."""
490
+ lines = [
491
+ "# Documentation Index\n\n",
492
+ "This is the complete documentation index for the project.\n\n",
493
+ ]
494
+
495
+ if api_docs:
496
+ lines.append("## API Documentation\n\n")
497
+ for doc in sorted(api_docs):
498
+ relative_path = doc.relative_to(self.docs_dir)
499
+ lines.append(f"- [{doc.stem}]({relative_path})\n")
500
+ lines.append("\n")
501
+
502
+ if guide_docs:
503
+ lines.append("## User Guides\n\n")
504
+ for doc in sorted(guide_docs):
505
+ relative_path = doc.relative_to(self.docs_dir)
506
+ lines.append(f"- [{doc.stem}]({relative_path})\n")
507
+ lines.append("\n")
508
+
509
+ if root_docs:
510
+ lines.append("## Additional Documentation\n\n")
511
+ for doc in sorted(root_docs):
512
+ lines.append(f"- [{doc.stem}]({doc.name})\n")
513
+ lines.append("\n")
514
+
515
+ return "".join(lines)
516
+
517
+ def _generate_protocol_documentation(self, protocols: dict[str, t.Any]) -> str:
518
+ """Generate focused protocol documentation."""
519
+ lines = ["# Protocol Reference\n\n"]
520
+ lines.append(
521
+ "This document describes all protocol interfaces used in the codebase.\n\n"
522
+ )
523
+
524
+ for protocol_name, protocol_info in sorted(protocols.items()):
525
+ lines.append(f"## {protocol_name}\n\n")
526
+
527
+ description = protocol_info.get("docstring", {}).get(
528
+ "description", "No description provided."
529
+ )
530
+ lines.append(f"{description}\n\n")
531
+
532
+ if protocol_info.get("runtime_checkable"):
533
+ lines.append("**Runtime Checkable:** Yes\n\n")
534
+
535
+ methods = protocol_info.get("methods", [])
536
+ if methods:
537
+ lines.append("### Required Methods\n\n")
538
+ for method in methods:
539
+ lines.append(f"#### `{method['name']}`\n\n")
540
+ method_desc = method.get("docstring", {}).get(
541
+ "description", "No description provided."
542
+ )
543
+ lines.append(f"{method_desc}\n\n")
544
+
545
+ # Add method signature
546
+ params = method.get("parameters", [])
547
+ param_strings = []
548
+ for param in params:
549
+ param_type = param.get("annotation", "Any")
550
+ param_strings.append(f"{param['name']}: {param_type}")
551
+
552
+ return_type = method.get("return_annotation", "Any")
553
+ signature = f"def {method['name']}({', '.join(param_strings)}) -> {return_type}"
554
+ lines.append(f"```python\n{signature}\n```\n\n")
555
+
556
+ lines.append("---\n\n")
557
+
558
+ return "".join(lines)
559
+
560
+ def _generate_service_documentation(self, services: dict[str, t.Any]) -> str:
561
+ """Generate focused service documentation."""
562
+ lines = ["# Service Reference\n\n"]
563
+ lines.append(
564
+ "This document describes all service implementations in the codebase.\n\n"
565
+ )
566
+
567
+ for service_name, service_info in sorted(services.items()):
568
+ service_section = self._generate_service_section(service_name, service_info)
569
+ lines.extend(service_section)
570
+
571
+ return "".join(lines)
572
+
573
+ def _generate_service_section(
574
+ self, service_name: str, service_info: dict[str, t.Any]
575
+ ) -> list[str]:
576
+ """Generate documentation section for a single service."""
577
+ lines: list[str] = []
578
+ lines.extend(
579
+ (
580
+ f"## {service_name}\n\n",
581
+ f"**Location:** `{service_info.get('path', 'Unknown')}`\n\n",
582
+ )
583
+ )
584
+
585
+ # Add protocols implemented
586
+ if service_info.get("protocols_implemented"):
587
+ protocol_lines = self._generate_protocols_implemented(
588
+ service_info["protocols_implemented"]
589
+ )
590
+ lines.extend(protocol_lines)
591
+
592
+ # Add class documentation
593
+ class_lines = self._generate_service_classes(service_info.get("classes", []))
594
+ lines.extend(class_lines)
595
+
596
+ return lines
597
+
598
+ def _generate_protocols_implemented(self, protocols: list[str]) -> list[str]:
599
+ """Generate protocols implemented section."""
600
+ lines = ["**Implements:**\n"]
601
+ for protocol in protocols:
602
+ lines.append(f"- {protocol}\n")
603
+ lines.append("\n")
604
+ return lines
605
+
606
+ def _generate_service_classes(self, classes: list[dict[str, t.Any]]) -> list[str]:
607
+ """Generate documentation for service classes."""
608
+ lines = []
609
+
610
+ for class_info in classes:
611
+ lines.append(f"### {class_info['name']}\n\n")
612
+ description = class_info.get("docstring", {}).get(
613
+ "description", "No description provided."
614
+ )
615
+ lines.append(f"{description}\n\n")
616
+
617
+ # Add public methods
618
+ public_method_lines = self._generate_public_methods(
619
+ class_info.get("methods", [])
620
+ )
621
+ lines.extend(public_method_lines)
622
+
623
+ return lines
624
+
625
+ def _generate_public_methods(self, methods: list[dict[str, t.Any]]) -> list[str]:
626
+ """Generate public methods documentation."""
627
+ public_methods = [m for m in methods if m.get("visibility") == "public"]
628
+
629
+ if not public_methods:
630
+ return []
631
+
632
+ lines = ["**Public Methods:**\n"]
633
+ for method in public_methods:
634
+ method_desc = method.get("docstring", {}).get("description", "")
635
+ lines.append(f"- `{method['name']}`: {method_desc}\n")
636
+ lines.append("\n")
637
+
638
+ return lines
639
+
640
+ def _generate_cli_documentation(self, commands: dict[str, t.Any]) -> str:
641
+ """Generate CLI reference documentation."""
642
+ lines = ["# CLI Reference\n\n"]
643
+ lines.append(
644
+ "This document describes all command-line options and usage patterns.\n\n"
645
+ )
646
+
647
+ for command_name, command_info in sorted(commands.items()):
648
+ lines.append(f"## {command_name}\n\n")
649
+
650
+ options = command_info.get("options", [])
651
+ if options:
652
+ lines.append("### Available Options\n\n")
653
+ for option in options:
654
+ lines.append(
655
+ f"- `--{option['name']}` ({option['type']}): {option.get('description', 'No description')}\n"
656
+ )
657
+ lines.append("\n")
658
+
659
+ return "".join(lines)
660
+
661
+ def _format_cross_references(self, cross_refs: dict[str, list[str]]) -> str:
662
+ """Format cross-references into markdown."""
663
+ lines = ["# Cross References\n\n"]
664
+ lines.append(
665
+ "This document shows where API components are used throughout the codebase.\n\n"
666
+ )
667
+
668
+ for name, references in sorted(cross_refs.items()):
669
+ if references:
670
+ lines.extend((f"## {name}\n\n", "**Referenced in:**\n"))
671
+ for ref in references:
672
+ lines.append(f"- {ref}\n")
673
+ lines.append("\n")
674
+
675
+ return "".join(lines)