crackerjack 0.18.2__py3-none-any.whl → 0.45.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.
- crackerjack/README.md +19 -0
- crackerjack/__init__.py +96 -2
- crackerjack/__main__.py +637 -138
- crackerjack/adapters/README.md +18 -0
- crackerjack/adapters/__init__.py +39 -0
- crackerjack/adapters/_output_paths.py +167 -0
- crackerjack/adapters/_qa_adapter_base.py +309 -0
- crackerjack/adapters/_tool_adapter_base.py +706 -0
- crackerjack/adapters/ai/README.md +65 -0
- crackerjack/adapters/ai/__init__.py +5 -0
- crackerjack/adapters/ai/claude.py +853 -0
- crackerjack/adapters/complexity/README.md +53 -0
- crackerjack/adapters/complexity/__init__.py +10 -0
- crackerjack/adapters/complexity/complexipy.py +641 -0
- crackerjack/adapters/dependency/__init__.py +22 -0
- crackerjack/adapters/dependency/pip_audit.py +418 -0
- crackerjack/adapters/format/README.md +72 -0
- crackerjack/adapters/format/__init__.py +11 -0
- crackerjack/adapters/format/mdformat.py +313 -0
- crackerjack/adapters/format/ruff.py +516 -0
- crackerjack/adapters/lint/README.md +47 -0
- crackerjack/adapters/lint/__init__.py +11 -0
- crackerjack/adapters/lint/codespell.py +273 -0
- crackerjack/adapters/lsp/README.md +49 -0
- crackerjack/adapters/lsp/__init__.py +27 -0
- crackerjack/adapters/lsp/_base.py +194 -0
- crackerjack/adapters/lsp/_client.py +358 -0
- crackerjack/adapters/lsp/_manager.py +193 -0
- crackerjack/adapters/lsp/skylos.py +283 -0
- crackerjack/adapters/lsp/zuban.py +557 -0
- crackerjack/adapters/refactor/README.md +59 -0
- crackerjack/adapters/refactor/__init__.py +12 -0
- crackerjack/adapters/refactor/creosote.py +318 -0
- crackerjack/adapters/refactor/refurb.py +406 -0
- crackerjack/adapters/refactor/skylos.py +494 -0
- crackerjack/adapters/sast/README.md +132 -0
- crackerjack/adapters/sast/__init__.py +32 -0
- crackerjack/adapters/sast/_base.py +201 -0
- crackerjack/adapters/sast/bandit.py +423 -0
- crackerjack/adapters/sast/pyscn.py +405 -0
- crackerjack/adapters/sast/semgrep.py +241 -0
- crackerjack/adapters/security/README.md +111 -0
- crackerjack/adapters/security/__init__.py +17 -0
- crackerjack/adapters/security/gitleaks.py +339 -0
- crackerjack/adapters/type/README.md +52 -0
- crackerjack/adapters/type/__init__.py +12 -0
- crackerjack/adapters/type/pyrefly.py +402 -0
- crackerjack/adapters/type/ty.py +402 -0
- crackerjack/adapters/type/zuban.py +522 -0
- crackerjack/adapters/utility/README.md +51 -0
- crackerjack/adapters/utility/__init__.py +10 -0
- crackerjack/adapters/utility/checks.py +884 -0
- crackerjack/agents/README.md +264 -0
- crackerjack/agents/__init__.py +66 -0
- crackerjack/agents/architect_agent.py +238 -0
- crackerjack/agents/base.py +167 -0
- crackerjack/agents/claude_code_bridge.py +641 -0
- crackerjack/agents/coordinator.py +600 -0
- crackerjack/agents/documentation_agent.py +520 -0
- crackerjack/agents/dry_agent.py +585 -0
- crackerjack/agents/enhanced_coordinator.py +279 -0
- crackerjack/agents/enhanced_proactive_agent.py +185 -0
- crackerjack/agents/error_middleware.py +53 -0
- crackerjack/agents/formatting_agent.py +230 -0
- crackerjack/agents/helpers/__init__.py +9 -0
- crackerjack/agents/helpers/performance/__init__.py +22 -0
- crackerjack/agents/helpers/performance/performance_ast_analyzer.py +357 -0
- crackerjack/agents/helpers/performance/performance_pattern_detector.py +909 -0
- crackerjack/agents/helpers/performance/performance_recommender.py +572 -0
- crackerjack/agents/helpers/refactoring/__init__.py +22 -0
- crackerjack/agents/helpers/refactoring/code_transformer.py +536 -0
- crackerjack/agents/helpers/refactoring/complexity_analyzer.py +344 -0
- crackerjack/agents/helpers/refactoring/dead_code_detector.py +437 -0
- crackerjack/agents/helpers/test_creation/__init__.py +19 -0
- crackerjack/agents/helpers/test_creation/test_ast_analyzer.py +216 -0
- crackerjack/agents/helpers/test_creation/test_coverage_analyzer.py +643 -0
- crackerjack/agents/helpers/test_creation/test_template_generator.py +1031 -0
- crackerjack/agents/import_optimization_agent.py +1181 -0
- crackerjack/agents/performance_agent.py +325 -0
- crackerjack/agents/performance_helpers.py +205 -0
- crackerjack/agents/proactive_agent.py +55 -0
- crackerjack/agents/refactoring_agent.py +511 -0
- crackerjack/agents/refactoring_helpers.py +247 -0
- crackerjack/agents/security_agent.py +793 -0
- crackerjack/agents/semantic_agent.py +479 -0
- crackerjack/agents/semantic_helpers.py +356 -0
- crackerjack/agents/test_creation_agent.py +570 -0
- crackerjack/agents/test_specialist_agent.py +526 -0
- crackerjack/agents/tracker.py +110 -0
- crackerjack/api.py +647 -0
- crackerjack/cli/README.md +394 -0
- crackerjack/cli/__init__.py +24 -0
- crackerjack/cli/cache_handlers.py +209 -0
- crackerjack/cli/cache_handlers_enhanced.py +680 -0
- crackerjack/cli/facade.py +162 -0
- crackerjack/cli/formatting.py +13 -0
- crackerjack/cli/handlers/__init__.py +85 -0
- crackerjack/cli/handlers/advanced.py +103 -0
- crackerjack/cli/handlers/ai_features.py +62 -0
- crackerjack/cli/handlers/analytics.py +479 -0
- crackerjack/cli/handlers/changelog.py +271 -0
- crackerjack/cli/handlers/config_handlers.py +16 -0
- crackerjack/cli/handlers/coverage.py +84 -0
- crackerjack/cli/handlers/documentation.py +280 -0
- crackerjack/cli/handlers/main_handlers.py +497 -0
- crackerjack/cli/handlers/monitoring.py +371 -0
- crackerjack/cli/handlers.py +700 -0
- crackerjack/cli/interactive.py +488 -0
- crackerjack/cli/options.py +1216 -0
- crackerjack/cli/semantic_handlers.py +292 -0
- crackerjack/cli/utils.py +19 -0
- crackerjack/cli/version.py +19 -0
- crackerjack/code_cleaner.py +1307 -0
- crackerjack/config/README.md +472 -0
- crackerjack/config/__init__.py +275 -0
- crackerjack/config/global_lock_config.py +207 -0
- crackerjack/config/hooks.py +390 -0
- crackerjack/config/loader.py +239 -0
- crackerjack/config/settings.py +141 -0
- crackerjack/config/tool_commands.py +331 -0
- crackerjack/core/README.md +393 -0
- crackerjack/core/__init__.py +0 -0
- crackerjack/core/async_workflow_orchestrator.py +738 -0
- crackerjack/core/autofix_coordinator.py +282 -0
- crackerjack/core/container.py +105 -0
- crackerjack/core/enhanced_container.py +583 -0
- crackerjack/core/file_lifecycle.py +472 -0
- crackerjack/core/performance.py +244 -0
- crackerjack/core/performance_monitor.py +357 -0
- crackerjack/core/phase_coordinator.py +1227 -0
- crackerjack/core/proactive_workflow.py +267 -0
- crackerjack/core/resource_manager.py +425 -0
- crackerjack/core/retry.py +275 -0
- crackerjack/core/service_watchdog.py +601 -0
- crackerjack/core/session_coordinator.py +239 -0
- crackerjack/core/timeout_manager.py +563 -0
- crackerjack/core/websocket_lifecycle.py +410 -0
- crackerjack/core/workflow/__init__.py +21 -0
- crackerjack/core/workflow/workflow_ai_coordinator.py +863 -0
- crackerjack/core/workflow/workflow_event_orchestrator.py +1107 -0
- crackerjack/core/workflow/workflow_issue_parser.py +714 -0
- crackerjack/core/workflow/workflow_phase_executor.py +1158 -0
- crackerjack/core/workflow/workflow_security_gates.py +400 -0
- crackerjack/core/workflow_orchestrator.py +2243 -0
- crackerjack/data/README.md +11 -0
- crackerjack/data/__init__.py +8 -0
- crackerjack/data/models.py +79 -0
- crackerjack/data/repository.py +210 -0
- crackerjack/decorators/README.md +180 -0
- crackerjack/decorators/__init__.py +35 -0
- crackerjack/decorators/error_handling.py +649 -0
- crackerjack/decorators/error_handling_decorators.py +334 -0
- crackerjack/decorators/helpers.py +58 -0
- crackerjack/decorators/patterns.py +281 -0
- crackerjack/decorators/utils.py +58 -0
- crackerjack/docs/INDEX.md +11 -0
- crackerjack/docs/README.md +11 -0
- crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
- crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
- crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
- crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
- crackerjack/docs/generated/api/SERVICES.md +1252 -0
- crackerjack/documentation/README.md +11 -0
- crackerjack/documentation/__init__.py +31 -0
- crackerjack/documentation/ai_templates.py +756 -0
- crackerjack/documentation/dual_output_generator.py +767 -0
- crackerjack/documentation/mkdocs_integration.py +518 -0
- crackerjack/documentation/reference_generator.py +1065 -0
- crackerjack/dynamic_config.py +678 -0
- crackerjack/errors.py +378 -0
- crackerjack/events/README.md +11 -0
- crackerjack/events/__init__.py +16 -0
- crackerjack/events/telemetry.py +175 -0
- crackerjack/events/workflow_bus.py +346 -0
- crackerjack/exceptions/README.md +301 -0
- crackerjack/exceptions/__init__.py +5 -0
- crackerjack/exceptions/config.py +4 -0
- crackerjack/exceptions/tool_execution_error.py +245 -0
- crackerjack/executors/README.md +591 -0
- crackerjack/executors/__init__.py +13 -0
- crackerjack/executors/async_hook_executor.py +938 -0
- crackerjack/executors/cached_hook_executor.py +316 -0
- crackerjack/executors/hook_executor.py +1295 -0
- crackerjack/executors/hook_lock_manager.py +708 -0
- crackerjack/executors/individual_hook_executor.py +739 -0
- crackerjack/executors/lsp_aware_hook_executor.py +349 -0
- crackerjack/executors/progress_hook_executor.py +282 -0
- crackerjack/executors/tool_proxy.py +433 -0
- crackerjack/hooks/README.md +485 -0
- crackerjack/hooks/lsp_hook.py +93 -0
- crackerjack/intelligence/README.md +557 -0
- crackerjack/intelligence/__init__.py +37 -0
- crackerjack/intelligence/adaptive_learning.py +693 -0
- crackerjack/intelligence/agent_orchestrator.py +485 -0
- crackerjack/intelligence/agent_registry.py +377 -0
- crackerjack/intelligence/agent_selector.py +439 -0
- crackerjack/intelligence/integration.py +250 -0
- crackerjack/interactive.py +719 -0
- crackerjack/managers/README.md +369 -0
- crackerjack/managers/__init__.py +11 -0
- crackerjack/managers/async_hook_manager.py +135 -0
- crackerjack/managers/hook_manager.py +585 -0
- crackerjack/managers/publish_manager.py +631 -0
- crackerjack/managers/test_command_builder.py +391 -0
- crackerjack/managers/test_executor.py +474 -0
- crackerjack/managers/test_manager.py +1357 -0
- crackerjack/managers/test_progress.py +187 -0
- crackerjack/mcp/README.md +374 -0
- crackerjack/mcp/__init__.py +0 -0
- crackerjack/mcp/cache.py +352 -0
- crackerjack/mcp/client_runner.py +121 -0
- crackerjack/mcp/context.py +802 -0
- crackerjack/mcp/dashboard.py +657 -0
- crackerjack/mcp/enhanced_progress_monitor.py +493 -0
- crackerjack/mcp/file_monitor.py +394 -0
- crackerjack/mcp/progress_components.py +607 -0
- crackerjack/mcp/progress_monitor.py +1016 -0
- crackerjack/mcp/rate_limiter.py +336 -0
- crackerjack/mcp/server.py +24 -0
- crackerjack/mcp/server_core.py +526 -0
- crackerjack/mcp/service_watchdog.py +505 -0
- crackerjack/mcp/state.py +407 -0
- crackerjack/mcp/task_manager.py +259 -0
- crackerjack/mcp/tools/README.md +27 -0
- crackerjack/mcp/tools/__init__.py +19 -0
- crackerjack/mcp/tools/core_tools.py +469 -0
- crackerjack/mcp/tools/error_analyzer.py +283 -0
- crackerjack/mcp/tools/execution_tools.py +384 -0
- crackerjack/mcp/tools/intelligence_tool_registry.py +46 -0
- crackerjack/mcp/tools/intelligence_tools.py +264 -0
- crackerjack/mcp/tools/monitoring_tools.py +628 -0
- crackerjack/mcp/tools/proactive_tools.py +367 -0
- crackerjack/mcp/tools/progress_tools.py +222 -0
- crackerjack/mcp/tools/semantic_tools.py +584 -0
- crackerjack/mcp/tools/utility_tools.py +358 -0
- crackerjack/mcp/tools/workflow_executor.py +699 -0
- crackerjack/mcp/websocket/README.md +31 -0
- crackerjack/mcp/websocket/__init__.py +14 -0
- crackerjack/mcp/websocket/app.py +54 -0
- crackerjack/mcp/websocket/endpoints.py +492 -0
- crackerjack/mcp/websocket/event_bridge.py +188 -0
- crackerjack/mcp/websocket/jobs.py +406 -0
- crackerjack/mcp/websocket/monitoring/__init__.py +25 -0
- crackerjack/mcp/websocket/monitoring/api/__init__.py +19 -0
- crackerjack/mcp/websocket/monitoring/api/dependencies.py +141 -0
- crackerjack/mcp/websocket/monitoring/api/heatmap.py +154 -0
- crackerjack/mcp/websocket/monitoring/api/intelligence.py +199 -0
- crackerjack/mcp/websocket/monitoring/api/metrics.py +203 -0
- crackerjack/mcp/websocket/monitoring/api/telemetry.py +101 -0
- crackerjack/mcp/websocket/monitoring/dashboard.py +18 -0
- crackerjack/mcp/websocket/monitoring/factory.py +109 -0
- crackerjack/mcp/websocket/monitoring/filters.py +10 -0
- crackerjack/mcp/websocket/monitoring/metrics.py +64 -0
- crackerjack/mcp/websocket/monitoring/models.py +90 -0
- crackerjack/mcp/websocket/monitoring/utils.py +171 -0
- crackerjack/mcp/websocket/monitoring/websocket_manager.py +78 -0
- crackerjack/mcp/websocket/monitoring/websockets/__init__.py +17 -0
- crackerjack/mcp/websocket/monitoring/websockets/dependencies.py +126 -0
- crackerjack/mcp/websocket/monitoring/websockets/heatmap.py +176 -0
- crackerjack/mcp/websocket/monitoring/websockets/intelligence.py +291 -0
- crackerjack/mcp/websocket/monitoring/websockets/metrics.py +291 -0
- crackerjack/mcp/websocket/monitoring_endpoints.py +21 -0
- crackerjack/mcp/websocket/server.py +174 -0
- crackerjack/mcp/websocket/websocket_handler.py +276 -0
- crackerjack/mcp/websocket_server.py +10 -0
- crackerjack/models/README.md +308 -0
- crackerjack/models/__init__.py +40 -0
- crackerjack/models/config.py +730 -0
- crackerjack/models/config_adapter.py +265 -0
- crackerjack/models/protocols.py +1535 -0
- crackerjack/models/pydantic_models.py +320 -0
- crackerjack/models/qa_config.py +145 -0
- crackerjack/models/qa_results.py +134 -0
- crackerjack/models/resource_protocols.py +299 -0
- crackerjack/models/results.py +35 -0
- crackerjack/models/semantic_models.py +258 -0
- crackerjack/models/task.py +173 -0
- crackerjack/models/test_models.py +60 -0
- crackerjack/monitoring/README.md +11 -0
- crackerjack/monitoring/__init__.py +0 -0
- crackerjack/monitoring/ai_agent_watchdog.py +405 -0
- crackerjack/monitoring/metrics_collector.py +427 -0
- crackerjack/monitoring/regression_prevention.py +580 -0
- crackerjack/monitoring/websocket_server.py +406 -0
- crackerjack/orchestration/README.md +340 -0
- crackerjack/orchestration/__init__.py +43 -0
- crackerjack/orchestration/advanced_orchestrator.py +894 -0
- crackerjack/orchestration/cache/README.md +312 -0
- crackerjack/orchestration/cache/__init__.py +37 -0
- crackerjack/orchestration/cache/memory_cache.py +338 -0
- crackerjack/orchestration/cache/tool_proxy_cache.py +340 -0
- crackerjack/orchestration/config.py +297 -0
- crackerjack/orchestration/coverage_improvement.py +180 -0
- crackerjack/orchestration/execution_strategies.py +361 -0
- crackerjack/orchestration/hook_orchestrator.py +1398 -0
- crackerjack/orchestration/strategies/README.md +401 -0
- crackerjack/orchestration/strategies/__init__.py +39 -0
- crackerjack/orchestration/strategies/adaptive_strategy.py +630 -0
- crackerjack/orchestration/strategies/parallel_strategy.py +237 -0
- crackerjack/orchestration/strategies/sequential_strategy.py +299 -0
- crackerjack/orchestration/test_progress_streamer.py +647 -0
- crackerjack/plugins/README.md +11 -0
- crackerjack/plugins/__init__.py +15 -0
- crackerjack/plugins/base.py +200 -0
- crackerjack/plugins/hooks.py +254 -0
- crackerjack/plugins/loader.py +335 -0
- crackerjack/plugins/managers.py +264 -0
- crackerjack/py313.py +191 -0
- crackerjack/security/README.md +11 -0
- crackerjack/security/__init__.py +0 -0
- crackerjack/security/audit.py +197 -0
- crackerjack/services/README.md +374 -0
- crackerjack/services/__init__.py +9 -0
- crackerjack/services/ai/README.md +295 -0
- crackerjack/services/ai/__init__.py +7 -0
- crackerjack/services/ai/advanced_optimizer.py +878 -0
- crackerjack/services/ai/contextual_ai_assistant.py +542 -0
- crackerjack/services/ai/embeddings.py +444 -0
- crackerjack/services/ai/intelligent_commit.py +328 -0
- crackerjack/services/ai/predictive_analytics.py +510 -0
- crackerjack/services/anomaly_detector.py +392 -0
- crackerjack/services/api_extractor.py +617 -0
- crackerjack/services/backup_service.py +467 -0
- crackerjack/services/bounded_status_operations.py +530 -0
- crackerjack/services/cache.py +369 -0
- crackerjack/services/changelog_automation.py +399 -0
- crackerjack/services/command_execution_service.py +305 -0
- crackerjack/services/config_integrity.py +132 -0
- crackerjack/services/config_merge.py +546 -0
- crackerjack/services/config_service.py +198 -0
- crackerjack/services/config_template.py +493 -0
- crackerjack/services/coverage_badge_service.py +173 -0
- crackerjack/services/coverage_ratchet.py +381 -0
- crackerjack/services/debug.py +733 -0
- crackerjack/services/dependency_analyzer.py +460 -0
- crackerjack/services/dependency_monitor.py +622 -0
- crackerjack/services/documentation_generator.py +493 -0
- crackerjack/services/documentation_service.py +704 -0
- crackerjack/services/enhanced_filesystem.py +497 -0
- crackerjack/services/enterprise_optimizer.py +865 -0
- crackerjack/services/error_pattern_analyzer.py +676 -0
- crackerjack/services/file_filter.py +221 -0
- crackerjack/services/file_hasher.py +149 -0
- crackerjack/services/file_io_service.py +361 -0
- crackerjack/services/file_modifier.py +615 -0
- crackerjack/services/filesystem.py +381 -0
- crackerjack/services/git.py +422 -0
- crackerjack/services/health_metrics.py +615 -0
- crackerjack/services/heatmap_generator.py +744 -0
- crackerjack/services/incremental_executor.py +380 -0
- crackerjack/services/initialization.py +823 -0
- crackerjack/services/input_validator.py +668 -0
- crackerjack/services/intelligent_commit.py +327 -0
- crackerjack/services/log_manager.py +289 -0
- crackerjack/services/logging.py +228 -0
- crackerjack/services/lsp_client.py +628 -0
- crackerjack/services/memory_optimizer.py +414 -0
- crackerjack/services/metrics.py +587 -0
- crackerjack/services/monitoring/README.md +30 -0
- crackerjack/services/monitoring/__init__.py +9 -0
- crackerjack/services/monitoring/dependency_monitor.py +678 -0
- crackerjack/services/monitoring/error_pattern_analyzer.py +676 -0
- crackerjack/services/monitoring/health_metrics.py +716 -0
- crackerjack/services/monitoring/metrics.py +587 -0
- crackerjack/services/monitoring/performance_benchmarks.py +410 -0
- crackerjack/services/monitoring/performance_cache.py +388 -0
- crackerjack/services/monitoring/performance_monitor.py +569 -0
- crackerjack/services/parallel_executor.py +527 -0
- crackerjack/services/pattern_cache.py +333 -0
- crackerjack/services/pattern_detector.py +478 -0
- crackerjack/services/patterns/__init__.py +142 -0
- crackerjack/services/patterns/agents.py +107 -0
- crackerjack/services/patterns/code/__init__.py +15 -0
- crackerjack/services/patterns/code/detection.py +118 -0
- crackerjack/services/patterns/code/imports.py +107 -0
- crackerjack/services/patterns/code/paths.py +159 -0
- crackerjack/services/patterns/code/performance.py +119 -0
- crackerjack/services/patterns/code/replacement.py +36 -0
- crackerjack/services/patterns/core.py +212 -0
- crackerjack/services/patterns/documentation/__init__.py +14 -0
- crackerjack/services/patterns/documentation/badges_markdown.py +96 -0
- crackerjack/services/patterns/documentation/comments_blocks.py +83 -0
- crackerjack/services/patterns/documentation/docstrings.py +89 -0
- crackerjack/services/patterns/formatting.py +226 -0
- crackerjack/services/patterns/operations.py +339 -0
- crackerjack/services/patterns/security/__init__.py +23 -0
- crackerjack/services/patterns/security/code_injection.py +122 -0
- crackerjack/services/patterns/security/credentials.py +190 -0
- crackerjack/services/patterns/security/path_traversal.py +221 -0
- crackerjack/services/patterns/security/unsafe_operations.py +216 -0
- crackerjack/services/patterns/templates.py +62 -0
- crackerjack/services/patterns/testing/__init__.py +18 -0
- crackerjack/services/patterns/testing/error_patterns.py +107 -0
- crackerjack/services/patterns/testing/pytest_output.py +126 -0
- crackerjack/services/patterns/tool_output/__init__.py +16 -0
- crackerjack/services/patterns/tool_output/bandit.py +72 -0
- crackerjack/services/patterns/tool_output/other.py +97 -0
- crackerjack/services/patterns/tool_output/pyright.py +67 -0
- crackerjack/services/patterns/tool_output/ruff.py +44 -0
- crackerjack/services/patterns/url_sanitization.py +114 -0
- crackerjack/services/patterns/utilities.py +42 -0
- crackerjack/services/patterns/utils.py +339 -0
- crackerjack/services/patterns/validation.py +46 -0
- crackerjack/services/patterns/versioning.py +62 -0
- crackerjack/services/predictive_analytics.py +523 -0
- crackerjack/services/profiler.py +280 -0
- crackerjack/services/quality/README.md +415 -0
- crackerjack/services/quality/__init__.py +11 -0
- crackerjack/services/quality/anomaly_detector.py +392 -0
- crackerjack/services/quality/pattern_cache.py +333 -0
- crackerjack/services/quality/pattern_detector.py +479 -0
- crackerjack/services/quality/qa_orchestrator.py +491 -0
- crackerjack/services/quality/quality_baseline.py +395 -0
- crackerjack/services/quality/quality_baseline_enhanced.py +649 -0
- crackerjack/services/quality/quality_intelligence.py +949 -0
- crackerjack/services/regex_patterns.py +58 -0
- crackerjack/services/regex_utils.py +483 -0
- crackerjack/services/secure_path_utils.py +524 -0
- crackerjack/services/secure_status_formatter.py +450 -0
- crackerjack/services/secure_subprocess.py +635 -0
- crackerjack/services/security.py +239 -0
- crackerjack/services/security_logger.py +495 -0
- crackerjack/services/server_manager.py +411 -0
- crackerjack/services/smart_scheduling.py +167 -0
- crackerjack/services/status_authentication.py +460 -0
- crackerjack/services/status_security_manager.py +315 -0
- crackerjack/services/terminal_utils.py +0 -0
- crackerjack/services/thread_safe_status_collector.py +441 -0
- crackerjack/services/tool_filter.py +368 -0
- crackerjack/services/tool_version_service.py +43 -0
- crackerjack/services/unified_config.py +115 -0
- crackerjack/services/validation_rate_limiter.py +220 -0
- crackerjack/services/vector_store.py +689 -0
- crackerjack/services/version_analyzer.py +461 -0
- crackerjack/services/version_checker.py +223 -0
- crackerjack/services/websocket_resource_limiter.py +438 -0
- crackerjack/services/zuban_lsp_service.py +391 -0
- crackerjack/slash_commands/README.md +11 -0
- crackerjack/slash_commands/__init__.py +59 -0
- crackerjack/slash_commands/init.md +112 -0
- crackerjack/slash_commands/run.md +197 -0
- crackerjack/slash_commands/status.md +127 -0
- crackerjack/tools/README.md +11 -0
- crackerjack/tools/__init__.py +30 -0
- crackerjack/tools/_git_utils.py +105 -0
- crackerjack/tools/check_added_large_files.py +139 -0
- crackerjack/tools/check_ast.py +105 -0
- crackerjack/tools/check_json.py +103 -0
- crackerjack/tools/check_jsonschema.py +297 -0
- crackerjack/tools/check_toml.py +103 -0
- crackerjack/tools/check_yaml.py +110 -0
- crackerjack/tools/codespell_wrapper.py +72 -0
- crackerjack/tools/end_of_file_fixer.py +202 -0
- crackerjack/tools/format_json.py +128 -0
- crackerjack/tools/mdformat_wrapper.py +114 -0
- crackerjack/tools/trailing_whitespace.py +198 -0
- crackerjack/tools/validate_input_validator_patterns.py +236 -0
- crackerjack/tools/validate_regex_patterns.py +188 -0
- crackerjack/ui/README.md +11 -0
- crackerjack/ui/__init__.py +1 -0
- crackerjack/ui/dashboard_renderer.py +28 -0
- crackerjack/ui/templates/README.md +11 -0
- crackerjack/utils/console_utils.py +13 -0
- crackerjack/utils/dependency_guard.py +230 -0
- crackerjack/utils/retry_utils.py +275 -0
- crackerjack/workflows/README.md +590 -0
- crackerjack/workflows/__init__.py +46 -0
- crackerjack/workflows/actions.py +811 -0
- crackerjack/workflows/auto_fix.py +444 -0
- crackerjack/workflows/container_builder.py +499 -0
- crackerjack/workflows/definitions.py +443 -0
- crackerjack/workflows/engine.py +177 -0
- crackerjack/workflows/event_bridge.py +242 -0
- crackerjack-0.45.2.dist-info/METADATA +1678 -0
- crackerjack-0.45.2.dist-info/RECORD +478 -0
- {crackerjack-0.18.2.dist-info → crackerjack-0.45.2.dist-info}/WHEEL +1 -1
- crackerjack-0.45.2.dist-info/entry_points.txt +2 -0
- crackerjack/.gitignore +0 -14
- crackerjack/.libcst.codemod.yaml +0 -18
- crackerjack/.pdm.toml +0 -1
- crackerjack/.pre-commit-config.yaml +0 -91
- crackerjack/.pytest_cache/.gitignore +0 -2
- crackerjack/.pytest_cache/CACHEDIR.TAG +0 -4
- crackerjack/.pytest_cache/README.md +0 -8
- crackerjack/.pytest_cache/v/cache/nodeids +0 -1
- crackerjack/.pytest_cache/v/cache/stepwise +0 -1
- crackerjack/.ruff_cache/.gitignore +0 -1
- crackerjack/.ruff_cache/0.1.11/3256171999636029978 +0 -0
- crackerjack/.ruff_cache/0.1.14/602324811142551221 +0 -0
- crackerjack/.ruff_cache/0.1.4/10355199064880463147 +0 -0
- crackerjack/.ruff_cache/0.1.6/15140459877605758699 +0 -0
- crackerjack/.ruff_cache/0.1.7/1790508110482614856 +0 -0
- crackerjack/.ruff_cache/0.1.9/17041001205004563469 +0 -0
- crackerjack/.ruff_cache/0.11.2/4070660268492669020 +0 -0
- crackerjack/.ruff_cache/0.11.3/9818742842212983150 +0 -0
- crackerjack/.ruff_cache/0.11.4/9818742842212983150 +0 -0
- crackerjack/.ruff_cache/0.11.6/3557596832929915217 +0 -0
- crackerjack/.ruff_cache/0.11.7/10386934055395314831 +0 -0
- crackerjack/.ruff_cache/0.11.7/3557596832929915217 +0 -0
- crackerjack/.ruff_cache/0.11.8/530407680854991027 +0 -0
- crackerjack/.ruff_cache/0.2.0/10047773857155985907 +0 -0
- crackerjack/.ruff_cache/0.2.1/8522267973936635051 +0 -0
- crackerjack/.ruff_cache/0.2.2/18053836298936336950 +0 -0
- crackerjack/.ruff_cache/0.3.0/12548816621480535786 +0 -0
- crackerjack/.ruff_cache/0.3.3/11081883392474770722 +0 -0
- crackerjack/.ruff_cache/0.3.4/676973378459347183 +0 -0
- crackerjack/.ruff_cache/0.3.5/16311176246009842383 +0 -0
- crackerjack/.ruff_cache/0.5.7/1493622539551733492 +0 -0
- crackerjack/.ruff_cache/0.5.7/6231957614044513175 +0 -0
- crackerjack/.ruff_cache/0.5.7/9932762556785938009 +0 -0
- crackerjack/.ruff_cache/0.6.0/11982804814124138945 +0 -0
- crackerjack/.ruff_cache/0.6.0/12055761203849489982 +0 -0
- crackerjack/.ruff_cache/0.6.2/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.4/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.5/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.7/3657366982708166874 +0 -0
- crackerjack/.ruff_cache/0.6.9/285614542852677309 +0 -0
- crackerjack/.ruff_cache/0.7.1/1024065805990144819 +0 -0
- crackerjack/.ruff_cache/0.7.1/285614542852677309 +0 -0
- crackerjack/.ruff_cache/0.7.3/16061516852537040135 +0 -0
- crackerjack/.ruff_cache/0.8.4/16354268377385700367 +0 -0
- crackerjack/.ruff_cache/0.9.10/12813592349865671909 +0 -0
- crackerjack/.ruff_cache/0.9.10/923908772239632759 +0 -0
- crackerjack/.ruff_cache/0.9.3/13948373885254993391 +0 -0
- crackerjack/.ruff_cache/0.9.9/12813592349865671909 +0 -0
- crackerjack/.ruff_cache/0.9.9/8843823720003377982 +0 -0
- crackerjack/.ruff_cache/CACHEDIR.TAG +0 -1
- crackerjack/crackerjack.py +0 -855
- crackerjack/pyproject.toml +0 -214
- crackerjack-0.18.2.dist-info/METADATA +0 -420
- crackerjack-0.18.2.dist-info/RECORD +0 -59
- crackerjack-0.18.2.dist-info/entry_points.txt +0 -4
- {crackerjack-0.18.2.dist-info → crackerjack-0.45.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,1065 @@
|
|
|
1
|
+
"""Reference generator for comprehensive command documentation.
|
|
2
|
+
|
|
3
|
+
This module provides automatic generation of command reference documentation
|
|
4
|
+
from CLI definitions, including usage examples, parameter descriptions, and
|
|
5
|
+
workflow integration guides.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import ast
|
|
9
|
+
import typing as t
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from enum import Enum
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from ..models.protocols import (
|
|
16
|
+
ConfigManagerProtocol,
|
|
17
|
+
LoggerProtocol,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ReferenceFormat(Enum):
|
|
22
|
+
"""Formats for reference documentation."""
|
|
23
|
+
|
|
24
|
+
MARKDOWN = "markdown"
|
|
25
|
+
HTML = "html"
|
|
26
|
+
JSON = "json"
|
|
27
|
+
YAML = "yaml"
|
|
28
|
+
RST = "rst"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class ParameterInfo:
|
|
33
|
+
"""Information about a command parameter."""
|
|
34
|
+
|
|
35
|
+
name: str
|
|
36
|
+
type_hint: str
|
|
37
|
+
default_value: t.Any
|
|
38
|
+
description: str
|
|
39
|
+
required: bool = False
|
|
40
|
+
choices: list[str] | None = None
|
|
41
|
+
example: str | None = None
|
|
42
|
+
deprecated: bool = False
|
|
43
|
+
added_in_version: str | None = None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class CommandInfo:
|
|
48
|
+
"""Information about a CLI command."""
|
|
49
|
+
|
|
50
|
+
name: str
|
|
51
|
+
description: str
|
|
52
|
+
category: str
|
|
53
|
+
parameters: list[ParameterInfo] = field(default_factory=list)
|
|
54
|
+
examples: list[dict[str, str]] = field(default_factory=list)
|
|
55
|
+
related_commands: list[str] = field(default_factory=list)
|
|
56
|
+
aliases: list[str] = field(default_factory=list)
|
|
57
|
+
deprecated: bool = False
|
|
58
|
+
added_in_version: str | None = None
|
|
59
|
+
|
|
60
|
+
# Workflow integration
|
|
61
|
+
common_workflows: list[str] = field(default_factory=list)
|
|
62
|
+
prerequisites: list[str] = field(default_factory=list)
|
|
63
|
+
side_effects: list[str] = field(default_factory=list)
|
|
64
|
+
|
|
65
|
+
# AI optimization
|
|
66
|
+
ai_context: dict[str, t.Any] = field(default_factory=dict[str, t.Any])
|
|
67
|
+
success_patterns: list[str] = field(default_factory=list)
|
|
68
|
+
failure_patterns: list[str] = field(default_factory=list)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass
|
|
72
|
+
class CommandReference:
|
|
73
|
+
"""Complete command reference documentation."""
|
|
74
|
+
|
|
75
|
+
commands: dict[str, CommandInfo]
|
|
76
|
+
categories: dict[str, list[str]]
|
|
77
|
+
workflows: dict[str, list[str]]
|
|
78
|
+
generated_at: datetime = field(default_factory=datetime.now)
|
|
79
|
+
version: str = "unknown"
|
|
80
|
+
|
|
81
|
+
def get_commands_by_category(self, category: str) -> list[CommandInfo]:
|
|
82
|
+
"""Get all commands in a specific category.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
category: Category name
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
List of commands in category
|
|
89
|
+
"""
|
|
90
|
+
return [cmd for cmd in self.commands.values() if cmd.category == category]
|
|
91
|
+
|
|
92
|
+
def get_command_by_name(self, name: str) -> CommandInfo | None:
|
|
93
|
+
"""Get command by name or alias.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
name: Command name or alias
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
Command info if found, None otherwise
|
|
100
|
+
"""
|
|
101
|
+
# Direct name match
|
|
102
|
+
if name in self.commands:
|
|
103
|
+
return self.commands[name]
|
|
104
|
+
|
|
105
|
+
# Alias match
|
|
106
|
+
for cmd in self.commands.values():
|
|
107
|
+
if name in cmd.aliases:
|
|
108
|
+
return cmd
|
|
109
|
+
|
|
110
|
+
return None
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class ReferenceGenerator:
|
|
114
|
+
"""Generator for comprehensive command reference documentation."""
|
|
115
|
+
|
|
116
|
+
def __init__(
|
|
117
|
+
self,
|
|
118
|
+
config_manager: ConfigManagerProtocol,
|
|
119
|
+
logger: LoggerProtocol,
|
|
120
|
+
):
|
|
121
|
+
self.config_manager = config_manager
|
|
122
|
+
self.logger = logger
|
|
123
|
+
|
|
124
|
+
async def generate_reference(
|
|
125
|
+
self,
|
|
126
|
+
cli_module_path: str,
|
|
127
|
+
output_format: ReferenceFormat = ReferenceFormat.MARKDOWN,
|
|
128
|
+
include_examples: bool = True,
|
|
129
|
+
include_workflows: bool = True,
|
|
130
|
+
) -> CommandReference:
|
|
131
|
+
"""Generate command reference from CLI module.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
cli_module_path: Path to CLI module to analyze
|
|
135
|
+
output_format: Output format for documentation
|
|
136
|
+
include_examples: Whether to include usage examples
|
|
137
|
+
include_workflows: Whether to include workflow information
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
Generated command reference
|
|
141
|
+
"""
|
|
142
|
+
self.logger.info(f"Generating command reference from: {cli_module_path}")
|
|
143
|
+
|
|
144
|
+
# Analyze CLI module
|
|
145
|
+
commands = await self._analyze_cli_module(cli_module_path)
|
|
146
|
+
|
|
147
|
+
# Enhance with examples if requested
|
|
148
|
+
if include_examples:
|
|
149
|
+
commands = await self._enhance_with_examples(commands)
|
|
150
|
+
|
|
151
|
+
# Enhance with workflows if requested
|
|
152
|
+
if include_workflows:
|
|
153
|
+
commands = await self._enhance_with_workflows(commands)
|
|
154
|
+
|
|
155
|
+
# Categorize commands
|
|
156
|
+
categories = self._categorize_commands(commands)
|
|
157
|
+
|
|
158
|
+
# Generate workflows
|
|
159
|
+
workflows = self._generate_workflows(commands) if include_workflows else {}
|
|
160
|
+
|
|
161
|
+
reference = CommandReference(
|
|
162
|
+
commands=commands,
|
|
163
|
+
categories=categories,
|
|
164
|
+
workflows=workflows,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
self.logger.info(f"Generated reference for {len(commands)} commands")
|
|
168
|
+
return reference
|
|
169
|
+
|
|
170
|
+
async def render_reference(
|
|
171
|
+
self,
|
|
172
|
+
reference: CommandReference,
|
|
173
|
+
output_format: ReferenceFormat,
|
|
174
|
+
template_name: str | None = None,
|
|
175
|
+
) -> str:
|
|
176
|
+
"""Render command reference to specified format.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
reference: Command reference to render
|
|
180
|
+
output_format: Output format
|
|
181
|
+
template_name: Optional template name to use
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
Rendered reference documentation
|
|
185
|
+
"""
|
|
186
|
+
if output_format == ReferenceFormat.MARKDOWN:
|
|
187
|
+
return self._render_markdown(reference)
|
|
188
|
+
elif output_format == ReferenceFormat.HTML:
|
|
189
|
+
return self._render_html(reference)
|
|
190
|
+
elif output_format == ReferenceFormat.JSON:
|
|
191
|
+
return self._render_json(reference)
|
|
192
|
+
elif output_format == ReferenceFormat.YAML:
|
|
193
|
+
return self._render_yaml(reference)
|
|
194
|
+
elif output_format == ReferenceFormat.RST:
|
|
195
|
+
return self._render_rst(reference)
|
|
196
|
+
else:
|
|
197
|
+
raise ValueError(f"Unsupported format: {output_format}")
|
|
198
|
+
|
|
199
|
+
async def _analyze_cli_module(self, module_path: str) -> dict[str, CommandInfo]:
|
|
200
|
+
"""Analyze CLI module to extract command information.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
module_path: Path to CLI module
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
Dictionary of command name to CommandInfo
|
|
207
|
+
"""
|
|
208
|
+
commands = {}
|
|
209
|
+
|
|
210
|
+
try:
|
|
211
|
+
# Read and parse the module
|
|
212
|
+
module_file = Path(module_path)
|
|
213
|
+
if not module_file.exists():
|
|
214
|
+
raise FileNotFoundError(f"CLI module not found: {module_path}")
|
|
215
|
+
|
|
216
|
+
source_code = module_file.read_text()
|
|
217
|
+
|
|
218
|
+
# Parse AST
|
|
219
|
+
tree = ast.parse(source_code)
|
|
220
|
+
|
|
221
|
+
# Extract command information
|
|
222
|
+
commands = self._extract_commands_from_ast(tree)
|
|
223
|
+
|
|
224
|
+
except Exception as e:
|
|
225
|
+
self.logger.error(f"Failed to analyze CLI module: {e}")
|
|
226
|
+
|
|
227
|
+
return commands
|
|
228
|
+
|
|
229
|
+
def _extract_commands_from_ast(self, tree: ast.AST) -> dict[str, CommandInfo]:
|
|
230
|
+
"""Extract command information from AST.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
tree: Parsed AST
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
Dictionary of commands
|
|
237
|
+
"""
|
|
238
|
+
commands: dict[str, CommandInfo] = {}
|
|
239
|
+
visitor = self._create_command_visitor(commands)
|
|
240
|
+
visitor.visit(tree)
|
|
241
|
+
return commands
|
|
242
|
+
|
|
243
|
+
def _create_command_visitor(
|
|
244
|
+
self, commands: dict[str, CommandInfo]
|
|
245
|
+
) -> ast.NodeVisitor:
|
|
246
|
+
"""Create AST visitor for command extraction.
|
|
247
|
+
|
|
248
|
+
Args:
|
|
249
|
+
commands: Dictionary to populate with commands
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
Configured AST visitor
|
|
253
|
+
"""
|
|
254
|
+
|
|
255
|
+
class CommandVisitor(ast.NodeVisitor):
|
|
256
|
+
def __init__(self, generator: t.Any) -> None:
|
|
257
|
+
self.generator = generator
|
|
258
|
+
|
|
259
|
+
def visit_FunctionDef(self, node: ast.FunctionDef) -> None:
|
|
260
|
+
self.generator._process_function_node(node, commands)
|
|
261
|
+
self.generic_visit(node)
|
|
262
|
+
|
|
263
|
+
return CommandVisitor(self)
|
|
264
|
+
|
|
265
|
+
def _process_function_node(
|
|
266
|
+
self, node: ast.FunctionDef, commands: dict[str, CommandInfo]
|
|
267
|
+
) -> None:
|
|
268
|
+
"""Process function node for command extraction.
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
node: Function definition node
|
|
272
|
+
commands: Commands dictionary to update
|
|
273
|
+
"""
|
|
274
|
+
for decorator in node.decorator_list:
|
|
275
|
+
if self._is_command_decorator(decorator):
|
|
276
|
+
command_info = self._extract_command_from_function(node)
|
|
277
|
+
if command_info:
|
|
278
|
+
commands[command_info.name] = command_info
|
|
279
|
+
|
|
280
|
+
def _is_command_decorator(self, decorator: ast.AST) -> bool:
|
|
281
|
+
"""Check if decorator indicates a CLI command.
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
decorator: AST decorator node
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
True if command decorator
|
|
288
|
+
"""
|
|
289
|
+
if isinstance(decorator, ast.Name):
|
|
290
|
+
return decorator.id in ("command", "click_command")
|
|
291
|
+
elif isinstance(decorator, ast.Attribute):
|
|
292
|
+
return decorator.attr in ("command", "callback")
|
|
293
|
+
return False
|
|
294
|
+
|
|
295
|
+
def _extract_command_from_function(
|
|
296
|
+
self, node: ast.FunctionDef
|
|
297
|
+
) -> CommandInfo | None:
|
|
298
|
+
"""Extract command info from function definition.
|
|
299
|
+
|
|
300
|
+
Args:
|
|
301
|
+
node: Function definition node
|
|
302
|
+
|
|
303
|
+
Returns:
|
|
304
|
+
Command info or None if extraction fails
|
|
305
|
+
"""
|
|
306
|
+
try:
|
|
307
|
+
command_name = node.name.replace("_", "-")
|
|
308
|
+
description = self._extract_docstring(node)
|
|
309
|
+
parameters = self._extract_function_parameters(node)
|
|
310
|
+
|
|
311
|
+
return CommandInfo(
|
|
312
|
+
name=command_name,
|
|
313
|
+
description=description or f"Execute {command_name}",
|
|
314
|
+
category="general",
|
|
315
|
+
parameters=parameters,
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
except Exception as e:
|
|
319
|
+
self.logger.warning(f"Failed to extract command {node.name}: {e}")
|
|
320
|
+
return None
|
|
321
|
+
|
|
322
|
+
def _extract_function_parameters(
|
|
323
|
+
self, node: ast.FunctionDef
|
|
324
|
+
) -> list[ParameterInfo]:
|
|
325
|
+
"""Extract parameter information from function.
|
|
326
|
+
|
|
327
|
+
Args:
|
|
328
|
+
node: Function definition node
|
|
329
|
+
|
|
330
|
+
Returns:
|
|
331
|
+
List of parameter information
|
|
332
|
+
"""
|
|
333
|
+
parameters = []
|
|
334
|
+
for arg in node.args.args:
|
|
335
|
+
if arg.arg != "self":
|
|
336
|
+
param_info = self._extract_parameter_info(arg, node)
|
|
337
|
+
parameters.append(param_info)
|
|
338
|
+
return parameters
|
|
339
|
+
|
|
340
|
+
def _extract_docstring(self, node: ast.FunctionDef) -> str | None:
|
|
341
|
+
"""Extract docstring from function.
|
|
342
|
+
|
|
343
|
+
Args:
|
|
344
|
+
node: Function definition node
|
|
345
|
+
|
|
346
|
+
Returns:
|
|
347
|
+
Docstring or None
|
|
348
|
+
"""
|
|
349
|
+
if (
|
|
350
|
+
node.body
|
|
351
|
+
and isinstance(node.body[0], ast.Expr)
|
|
352
|
+
and isinstance(node.body[0].value, ast.Constant)
|
|
353
|
+
and isinstance(node.body[0].value.value, str)
|
|
354
|
+
):
|
|
355
|
+
return node.body[0].value.value.strip()
|
|
356
|
+
return None
|
|
357
|
+
|
|
358
|
+
def _extract_parameter_info(
|
|
359
|
+
self, arg: ast.arg, func_node: ast.FunctionDef
|
|
360
|
+
) -> ParameterInfo:
|
|
361
|
+
"""Extract parameter information.
|
|
362
|
+
|
|
363
|
+
Args:
|
|
364
|
+
arg: Function argument node
|
|
365
|
+
func_node: Parent function node
|
|
366
|
+
|
|
367
|
+
Returns:
|
|
368
|
+
Parameter information
|
|
369
|
+
"""
|
|
370
|
+
param_name = arg.arg.replace("_", "-")
|
|
371
|
+
type_hint = ast.unparse(arg.annotation) if arg.annotation else "Any"
|
|
372
|
+
default_value, required = self._extract_default_value(arg, func_node)
|
|
373
|
+
|
|
374
|
+
return ParameterInfo(
|
|
375
|
+
name=param_name,
|
|
376
|
+
type_hint=type_hint,
|
|
377
|
+
default_value=default_value,
|
|
378
|
+
description=f"Parameter: {param_name}",
|
|
379
|
+
required=required,
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
def _extract_default_value(
|
|
383
|
+
self, arg: ast.arg, func_node: ast.FunctionDef
|
|
384
|
+
) -> tuple[t.Any, bool]:
|
|
385
|
+
"""Extract default value and required status for parameter.
|
|
386
|
+
|
|
387
|
+
Args:
|
|
388
|
+
arg: Function argument node
|
|
389
|
+
func_node: Parent function node
|
|
390
|
+
|
|
391
|
+
Returns:
|
|
392
|
+
Tuple of (default_value, required)
|
|
393
|
+
"""
|
|
394
|
+
defaults_count = len(func_node.args.defaults)
|
|
395
|
+
args_count = len(func_node.args.args)
|
|
396
|
+
defaults_start = args_count - defaults_count
|
|
397
|
+
|
|
398
|
+
arg_index = func_node.args.args.index(arg)
|
|
399
|
+
if arg_index >= defaults_start:
|
|
400
|
+
return self._extract_argument_default(arg_index, defaults_start, func_node)
|
|
401
|
+
|
|
402
|
+
return None, True
|
|
403
|
+
|
|
404
|
+
def _extract_argument_default(
|
|
405
|
+
self, arg_index: int, defaults_start: int, func_node: ast.FunctionDef
|
|
406
|
+
) -> tuple[t.Any, bool]:
|
|
407
|
+
"""Extract default value for a specific argument.
|
|
408
|
+
|
|
409
|
+
Args:
|
|
410
|
+
arg_index: Index of the argument
|
|
411
|
+
defaults_start: Index where defaults start
|
|
412
|
+
func_node: Parent function node
|
|
413
|
+
|
|
414
|
+
Returns:
|
|
415
|
+
Tuple of (default_value, required)
|
|
416
|
+
"""
|
|
417
|
+
default_index = arg_index - defaults_start
|
|
418
|
+
default_node = func_node.args.defaults[default_index]
|
|
419
|
+
if isinstance(default_node, ast.Constant):
|
|
420
|
+
return default_node.value, False
|
|
421
|
+
return None, True
|
|
422
|
+
|
|
423
|
+
async def _enhance_with_examples(
|
|
424
|
+
self, commands: dict[str, CommandInfo]
|
|
425
|
+
) -> dict[str, CommandInfo]:
|
|
426
|
+
"""Enhance commands with usage examples.
|
|
427
|
+
|
|
428
|
+
Args:
|
|
429
|
+
commands: Commands to enhance
|
|
430
|
+
|
|
431
|
+
Returns:
|
|
432
|
+
Enhanced commands with examples
|
|
433
|
+
"""
|
|
434
|
+
for command in commands.values():
|
|
435
|
+
self._add_basic_example(command)
|
|
436
|
+
self._add_parameter_examples(command)
|
|
437
|
+
return commands
|
|
438
|
+
|
|
439
|
+
def _add_basic_example(self, command: CommandInfo) -> None:
|
|
440
|
+
"""Add a basic example for a command."""
|
|
441
|
+
basic_example = f"python -m crackerjack --{command.name}"
|
|
442
|
+
command.examples.append(
|
|
443
|
+
{
|
|
444
|
+
"description": f"Basic {command.name} usage",
|
|
445
|
+
"command": basic_example,
|
|
446
|
+
}
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
def _add_parameter_examples(self, command: CommandInfo) -> None:
|
|
450
|
+
"""Add parameter examples for a command."""
|
|
451
|
+
# Generate basic examples
|
|
452
|
+
basic_example = f"python -m crackerjack --{command.name}"
|
|
453
|
+
|
|
454
|
+
# Add parameter examples
|
|
455
|
+
param_examples = []
|
|
456
|
+
for param in command.parameters:
|
|
457
|
+
if not param.required and param.default_value is not None:
|
|
458
|
+
param_example = self._format_parameter_example(param)
|
|
459
|
+
if param_example:
|
|
460
|
+
param_examples.append(param_example)
|
|
461
|
+
|
|
462
|
+
if param_examples:
|
|
463
|
+
enhanced_example = f"{basic_example} {' '.join(param_examples)}"
|
|
464
|
+
command.examples.append(
|
|
465
|
+
{
|
|
466
|
+
"description": f"Using {command.name} with parameters",
|
|
467
|
+
"command": enhanced_example,
|
|
468
|
+
}
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
def _format_parameter_example(self, param: ParameterInfo) -> str | None:
|
|
472
|
+
"""Format a parameter example."""
|
|
473
|
+
if isinstance(param.default_value, bool):
|
|
474
|
+
return f"--{param.name}"
|
|
475
|
+
return f"--{param.name} {param.default_value}"
|
|
476
|
+
|
|
477
|
+
async def _enhance_with_workflows(
|
|
478
|
+
self, commands: dict[str, CommandInfo]
|
|
479
|
+
) -> dict[str, CommandInfo]:
|
|
480
|
+
"""Enhance commands with workflow information.
|
|
481
|
+
|
|
482
|
+
Args:
|
|
483
|
+
commands: Commands to enhance
|
|
484
|
+
|
|
485
|
+
Returns:
|
|
486
|
+
Enhanced commands with workflow info
|
|
487
|
+
"""
|
|
488
|
+
workflow_patterns = self._get_workflow_patterns()
|
|
489
|
+
|
|
490
|
+
for command in commands.values():
|
|
491
|
+
self._assign_command_workflows(command, workflow_patterns)
|
|
492
|
+
self._add_ai_context_to_command(command)
|
|
493
|
+
|
|
494
|
+
return commands
|
|
495
|
+
|
|
496
|
+
def _get_workflow_patterns(self) -> dict[str, list[str]]:
|
|
497
|
+
"""Get workflow patterns for command categorization.
|
|
498
|
+
|
|
499
|
+
Returns:
|
|
500
|
+
Dictionary mapping workflow names to pattern lists
|
|
501
|
+
"""
|
|
502
|
+
return {
|
|
503
|
+
"development": ["test", "format", "lint", "type-check"],
|
|
504
|
+
"release": ["version", "build", "publish", "tag"],
|
|
505
|
+
"maintenance": ["clean", "update", "optimize", "backup"],
|
|
506
|
+
"monitoring": ["status", "health", "metrics", "logs"],
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
def _assign_command_workflows(
|
|
510
|
+
self, command: CommandInfo, workflow_patterns: dict[str, list[str]]
|
|
511
|
+
) -> None:
|
|
512
|
+
"""Assign workflows to a command based on name patterns.
|
|
513
|
+
|
|
514
|
+
Args:
|
|
515
|
+
command: Command to assign workflows to
|
|
516
|
+
workflow_patterns: Workflow patterns to match against
|
|
517
|
+
"""
|
|
518
|
+
for workflow, patterns in workflow_patterns.items():
|
|
519
|
+
if any(pattern in command.name for pattern in patterns):
|
|
520
|
+
command.common_workflows.append(workflow)
|
|
521
|
+
|
|
522
|
+
def _add_ai_context_to_command(self, command: CommandInfo) -> None:
|
|
523
|
+
"""Add AI context to a command based on its purpose.
|
|
524
|
+
|
|
525
|
+
Args:
|
|
526
|
+
command: Command to enhance with AI context
|
|
527
|
+
"""
|
|
528
|
+
if "test" in command.name:
|
|
529
|
+
self._add_test_ai_context(command)
|
|
530
|
+
elif "format" in command.name or "lint" in command.name:
|
|
531
|
+
self._add_quality_ai_context(command)
|
|
532
|
+
|
|
533
|
+
def _add_test_ai_context(self, command: CommandInfo) -> None:
|
|
534
|
+
"""Add AI context for test-related commands."""
|
|
535
|
+
command.ai_context.update(
|
|
536
|
+
{
|
|
537
|
+
"purpose": "quality_assurance",
|
|
538
|
+
"automation_level": "high",
|
|
539
|
+
"ai_agent_compatible": True,
|
|
540
|
+
}
|
|
541
|
+
)
|
|
542
|
+
command.success_patterns.append("All tests passed")
|
|
543
|
+
command.failure_patterns.append("Test failures detected")
|
|
544
|
+
|
|
545
|
+
def _add_quality_ai_context(self, command: CommandInfo) -> None:
|
|
546
|
+
"""Add AI context for code quality commands."""
|
|
547
|
+
command.ai_context.update(
|
|
548
|
+
{
|
|
549
|
+
"purpose": "code_quality",
|
|
550
|
+
"automation_level": "high",
|
|
551
|
+
"ai_agent_compatible": True,
|
|
552
|
+
}
|
|
553
|
+
)
|
|
554
|
+
command.success_patterns.append("No formatting issues")
|
|
555
|
+
command.failure_patterns.append("Style violations found")
|
|
556
|
+
|
|
557
|
+
def _categorize_commands(
|
|
558
|
+
self, commands: dict[str, CommandInfo]
|
|
559
|
+
) -> dict[str, list[str]]:
|
|
560
|
+
"""Categorize commands by purpose.
|
|
561
|
+
|
|
562
|
+
Args:
|
|
563
|
+
commands: Commands to categorize
|
|
564
|
+
|
|
565
|
+
Returns:
|
|
566
|
+
Dictionary of category to command names
|
|
567
|
+
"""
|
|
568
|
+
categories: dict[str, list[str]] = {}
|
|
569
|
+
category_patterns = self._get_category_patterns()
|
|
570
|
+
|
|
571
|
+
for command in commands.values():
|
|
572
|
+
category = self._determine_command_category(command, category_patterns)
|
|
573
|
+
command.category = category
|
|
574
|
+
self._add_command_to_category(categories, category, command.name)
|
|
575
|
+
|
|
576
|
+
return categories
|
|
577
|
+
|
|
578
|
+
def _get_category_patterns(self) -> dict[str, list[str]]:
|
|
579
|
+
"""Get category patterns for command classification."""
|
|
580
|
+
return {
|
|
581
|
+
"development": ["test", "format", "lint", "check", "run"],
|
|
582
|
+
"server": ["server", "start", "stop", "restart", "monitor"],
|
|
583
|
+
"release": ["version", "bump", "publish", "build", "tag"],
|
|
584
|
+
"configuration": ["config", "init", "setup", "install"],
|
|
585
|
+
"utilities": ["clean", "help", "info", "status"],
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
def _determine_command_category(
|
|
589
|
+
self, command: CommandInfo, category_patterns: dict[str, list[str]]
|
|
590
|
+
) -> str:
|
|
591
|
+
"""Determine the category for a command based on patterns."""
|
|
592
|
+
for category, patterns in category_patterns.items():
|
|
593
|
+
if any(pattern in command.name for pattern in patterns):
|
|
594
|
+
return category
|
|
595
|
+
return "general"
|
|
596
|
+
|
|
597
|
+
def _add_command_to_category(
|
|
598
|
+
self, categories: dict[str, list[str]], category: str, command_name: str
|
|
599
|
+
) -> None:
|
|
600
|
+
"""Add command to the specified category."""
|
|
601
|
+
if category not in categories:
|
|
602
|
+
categories[category] = []
|
|
603
|
+
categories[category].append(command_name)
|
|
604
|
+
|
|
605
|
+
def _generate_workflows(
|
|
606
|
+
self, commands: dict[str, CommandInfo]
|
|
607
|
+
) -> dict[str, list[str]]:
|
|
608
|
+
"""Generate workflow sequences from commands.
|
|
609
|
+
|
|
610
|
+
Args:
|
|
611
|
+
commands: Available commands
|
|
612
|
+
|
|
613
|
+
Returns:
|
|
614
|
+
Dictionary of workflow name to command sequence
|
|
615
|
+
"""
|
|
616
|
+
workflows = {
|
|
617
|
+
"development_cycle": [
|
|
618
|
+
"format",
|
|
619
|
+
"lint",
|
|
620
|
+
"test",
|
|
621
|
+
"type-check",
|
|
622
|
+
],
|
|
623
|
+
"release_cycle": [
|
|
624
|
+
"test",
|
|
625
|
+
"lint",
|
|
626
|
+
"version-bump",
|
|
627
|
+
"build",
|
|
628
|
+
"publish",
|
|
629
|
+
],
|
|
630
|
+
"maintenance_cycle": [
|
|
631
|
+
"clean",
|
|
632
|
+
"update-dependencies",
|
|
633
|
+
"test",
|
|
634
|
+
"optimize",
|
|
635
|
+
],
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
# Filter workflows to only include available commands
|
|
639
|
+
available_workflows = {}
|
|
640
|
+
for workflow_name, command_sequence in workflows.items():
|
|
641
|
+
available_sequence = [
|
|
642
|
+
cmd
|
|
643
|
+
for cmd in command_sequence
|
|
644
|
+
if any(cmd in available_cmd.name for available_cmd in commands.values())
|
|
645
|
+
]
|
|
646
|
+
if available_sequence:
|
|
647
|
+
available_workflows[workflow_name] = available_sequence
|
|
648
|
+
|
|
649
|
+
return available_workflows
|
|
650
|
+
|
|
651
|
+
def _render_markdown(self, reference: CommandReference) -> str:
|
|
652
|
+
"""Render reference as Markdown.
|
|
653
|
+
|
|
654
|
+
Args:
|
|
655
|
+
reference: Command reference
|
|
656
|
+
|
|
657
|
+
Returns:
|
|
658
|
+
Markdown formatted reference
|
|
659
|
+
"""
|
|
660
|
+
lines = [
|
|
661
|
+
"# Command Reference",
|
|
662
|
+
"",
|
|
663
|
+
f"Generated: {reference.generated_at.strftime('%Y-%m-%d %H:%M:%S')}",
|
|
664
|
+
f"Version: {reference.version}",
|
|
665
|
+
"",
|
|
666
|
+
"## Categories",
|
|
667
|
+
"",
|
|
668
|
+
]
|
|
669
|
+
|
|
670
|
+
# Add table of contents
|
|
671
|
+
lines.extend(self._render_markdown_toc(reference.categories))
|
|
672
|
+
lines.append("")
|
|
673
|
+
|
|
674
|
+
# Add commands by category
|
|
675
|
+
lines.extend(self._render_markdown_categories(reference))
|
|
676
|
+
|
|
677
|
+
# Add workflows if present
|
|
678
|
+
if reference.workflows:
|
|
679
|
+
lines.extend(self._render_markdown_workflows(reference.workflows))
|
|
680
|
+
|
|
681
|
+
return "\n".join(lines)
|
|
682
|
+
|
|
683
|
+
def _render_markdown_toc(self, categories: dict[str, list[str]]) -> list[str]:
|
|
684
|
+
"""Render table of contents for markdown.
|
|
685
|
+
|
|
686
|
+
Args:
|
|
687
|
+
categories: Command categories
|
|
688
|
+
|
|
689
|
+
Returns:
|
|
690
|
+
List of TOC lines
|
|
691
|
+
"""
|
|
692
|
+
return [
|
|
693
|
+
f"- [{category.title()}](#{category.replace('_', '-')})"
|
|
694
|
+
for category in categories
|
|
695
|
+
]
|
|
696
|
+
|
|
697
|
+
def _render_markdown_categories(self, reference: CommandReference) -> list[str]:
|
|
698
|
+
"""Render command categories for markdown."""
|
|
699
|
+
category_lines = []
|
|
700
|
+
for category, command_names in reference.categories.items():
|
|
701
|
+
category_section = self._render_markdown_category(
|
|
702
|
+
category, reference.commands, command_names
|
|
703
|
+
)
|
|
704
|
+
category_lines.extend(category_section)
|
|
705
|
+
return category_lines
|
|
706
|
+
|
|
707
|
+
def _render_markdown_category(
|
|
708
|
+
self, category: str, commands: dict[str, CommandInfo], command_names: list[str]
|
|
709
|
+
) -> list[str]:
|
|
710
|
+
"""Render markdown for a single category."""
|
|
711
|
+
category_lines = [
|
|
712
|
+
f"## {category.title()}",
|
|
713
|
+
"",
|
|
714
|
+
]
|
|
715
|
+
|
|
716
|
+
for command_name in command_names:
|
|
717
|
+
command = commands[command_name]
|
|
718
|
+
command_lines = self._render_command_markdown(command)
|
|
719
|
+
category_lines.extend(command_lines)
|
|
720
|
+
|
|
721
|
+
return category_lines
|
|
722
|
+
|
|
723
|
+
def _render_markdown_workflows(self, workflows: dict[str, list[str]]) -> list[str]:
|
|
724
|
+
"""Render workflows section for markdown.
|
|
725
|
+
|
|
726
|
+
Args:
|
|
727
|
+
workflows: Workflow definitions
|
|
728
|
+
|
|
729
|
+
Returns:
|
|
730
|
+
List of workflow section lines
|
|
731
|
+
"""
|
|
732
|
+
workflow_lines = [
|
|
733
|
+
"## Workflows",
|
|
734
|
+
"",
|
|
735
|
+
]
|
|
736
|
+
|
|
737
|
+
for workflow_name, command_sequence in workflows.items():
|
|
738
|
+
workflow_lines.extend(
|
|
739
|
+
[
|
|
740
|
+
f"### {workflow_name.replace('_', ' ').title()}",
|
|
741
|
+
"",
|
|
742
|
+
]
|
|
743
|
+
)
|
|
744
|
+
|
|
745
|
+
for i, cmd in enumerate(command_sequence, 1):
|
|
746
|
+
workflow_lines.append(f"{i}. `{cmd}`")
|
|
747
|
+
|
|
748
|
+
workflow_lines.append("")
|
|
749
|
+
|
|
750
|
+
return workflow_lines
|
|
751
|
+
|
|
752
|
+
def _render_command_markdown(self, command: CommandInfo) -> list[str]:
|
|
753
|
+
"""Render single command as Markdown."""
|
|
754
|
+
lines = [
|
|
755
|
+
f"### `{command.name}`",
|
|
756
|
+
"",
|
|
757
|
+
command.description,
|
|
758
|
+
"",
|
|
759
|
+
]
|
|
760
|
+
|
|
761
|
+
# Add parameters section
|
|
762
|
+
if command.parameters:
|
|
763
|
+
param_lines = self._render_command_parameters_markdown(command.parameters)
|
|
764
|
+
lines.extend(param_lines)
|
|
765
|
+
|
|
766
|
+
# Add examples section
|
|
767
|
+
if command.examples:
|
|
768
|
+
example_lines = self._render_command_examples_markdown(command.examples)
|
|
769
|
+
lines.extend(example_lines)
|
|
770
|
+
|
|
771
|
+
# Add related commands section
|
|
772
|
+
if command.related_commands:
|
|
773
|
+
related_lines = self._render_command_related_markdown(
|
|
774
|
+
command.related_commands
|
|
775
|
+
)
|
|
776
|
+
lines.extend(related_lines)
|
|
777
|
+
|
|
778
|
+
return lines
|
|
779
|
+
|
|
780
|
+
def _render_command_parameters_markdown(
|
|
781
|
+
self, parameters: list[ParameterInfo]
|
|
782
|
+
) -> list[str]:
|
|
783
|
+
"""Render command parameters for markdown."""
|
|
784
|
+
param_lines = [
|
|
785
|
+
"**Parameters:**",
|
|
786
|
+
"",
|
|
787
|
+
]
|
|
788
|
+
|
|
789
|
+
for param in parameters:
|
|
790
|
+
param_line = self._format_parameter_line(param)
|
|
791
|
+
param_lines.append(param_line)
|
|
792
|
+
|
|
793
|
+
param_lines.append("")
|
|
794
|
+
return param_lines
|
|
795
|
+
|
|
796
|
+
def _format_parameter_line(self, param: ParameterInfo) -> str:
|
|
797
|
+
"""Format a single parameter line."""
|
|
798
|
+
required_str = " (required)" if param.required else ""
|
|
799
|
+
default_str = (
|
|
800
|
+
f" (default: {param.default_value})"
|
|
801
|
+
if param.default_value is not None
|
|
802
|
+
else ""
|
|
803
|
+
)
|
|
804
|
+
return f"- `--{param.name}` ({param.type_hint}){required_str}{default_str}: {param.description}"
|
|
805
|
+
|
|
806
|
+
def _render_command_examples_markdown(
|
|
807
|
+
self, examples: list[dict[str, str]]
|
|
808
|
+
) -> list[str]:
|
|
809
|
+
"""Render command examples for markdown.
|
|
810
|
+
|
|
811
|
+
Args:
|
|
812
|
+
examples: List of examples to render
|
|
813
|
+
|
|
814
|
+
Returns:
|
|
815
|
+
List of examples section lines
|
|
816
|
+
"""
|
|
817
|
+
example_lines = [
|
|
818
|
+
"**Examples:**",
|
|
819
|
+
"",
|
|
820
|
+
]
|
|
821
|
+
|
|
822
|
+
for example in examples:
|
|
823
|
+
example_lines.extend(
|
|
824
|
+
[
|
|
825
|
+
f"*{example['description']}:*",
|
|
826
|
+
"```bash",
|
|
827
|
+
example["command"],
|
|
828
|
+
"```",
|
|
829
|
+
"",
|
|
830
|
+
]
|
|
831
|
+
)
|
|
832
|
+
|
|
833
|
+
return example_lines
|
|
834
|
+
|
|
835
|
+
def _render_command_related_markdown(
|
|
836
|
+
self, related_commands: list[str]
|
|
837
|
+
) -> list[str]:
|
|
838
|
+
"""Render related commands for markdown.
|
|
839
|
+
|
|
840
|
+
Args:
|
|
841
|
+
related_commands: List of related command names
|
|
842
|
+
|
|
843
|
+
Returns:
|
|
844
|
+
List of related commands section lines
|
|
845
|
+
"""
|
|
846
|
+
return [
|
|
847
|
+
"**Related commands:** "
|
|
848
|
+
+ ", ".join(f"`{cmd}`" for cmd in related_commands),
|
|
849
|
+
"",
|
|
850
|
+
]
|
|
851
|
+
|
|
852
|
+
def _render_html(self, reference: CommandReference) -> str:
|
|
853
|
+
"""Render reference as HTML."""
|
|
854
|
+
html_parts = [
|
|
855
|
+
self._render_html_header(
|
|
856
|
+
reference.generated_at.strftime("%Y-%m-%d %H:%M:%S")
|
|
857
|
+
),
|
|
858
|
+
self._render_html_commands(reference),
|
|
859
|
+
"</body></html>",
|
|
860
|
+
]
|
|
861
|
+
return "".join(html_parts)
|
|
862
|
+
|
|
863
|
+
def _render_html_header(self, generated_at: str) -> str:
|
|
864
|
+
"""Render HTML header with styles and metadata."""
|
|
865
|
+
return f"""<!DOCTYPE html>
|
|
866
|
+
<html>
|
|
867
|
+
<head>
|
|
868
|
+
<title>Command Reference</title>
|
|
869
|
+
<style>
|
|
870
|
+
body {{ font-family: Arial, sans-serif; margin: 40px; }}
|
|
871
|
+
.command {{ margin-bottom: 2em; }}
|
|
872
|
+
.parameter {{ margin-left: 1em; }}
|
|
873
|
+
code {{ background-color: #f5f5f5; padding: 2px 4px; }}
|
|
874
|
+
pre {{ background-color: #f5f5f5; padding: 10px; }}
|
|
875
|
+
</style>
|
|
876
|
+
</head>
|
|
877
|
+
<body>
|
|
878
|
+
<h1>Command Reference</h1>
|
|
879
|
+
<p>Generated: {generated_at}</p>
|
|
880
|
+
"""
|
|
881
|
+
|
|
882
|
+
def _render_html_commands(self, reference: CommandReference) -> str:
|
|
883
|
+
"""Render HTML commands by category."""
|
|
884
|
+
html_parts = []
|
|
885
|
+
for category, command_names in reference.categories.items():
|
|
886
|
+
category_html = self._render_html_category(
|
|
887
|
+
category, reference.commands, command_names
|
|
888
|
+
)
|
|
889
|
+
html_parts.append(category_html)
|
|
890
|
+
return "".join(html_parts)
|
|
891
|
+
|
|
892
|
+
def _render_html_category(
|
|
893
|
+
self, category: str, commands: dict[str, CommandInfo], command_names: list[str]
|
|
894
|
+
) -> str:
|
|
895
|
+
"""Render HTML for a single category."""
|
|
896
|
+
html = f"<h2>{category.title()}</h2>"
|
|
897
|
+
html += self._render_html_category_commands(commands, command_names)
|
|
898
|
+
return html
|
|
899
|
+
|
|
900
|
+
def _render_html_category_commands(
|
|
901
|
+
self, commands: dict[str, CommandInfo], command_names: list[str]
|
|
902
|
+
) -> str:
|
|
903
|
+
"""Render HTML for commands in a category."""
|
|
904
|
+
html_parts = []
|
|
905
|
+
for command_name in command_names:
|
|
906
|
+
command = commands[command_name]
|
|
907
|
+
command_html = self._render_single_html_command(command)
|
|
908
|
+
html_parts.append(command_html)
|
|
909
|
+
return "".join(html_parts)
|
|
910
|
+
|
|
911
|
+
def _render_single_html_command(self, command: CommandInfo) -> str:
|
|
912
|
+
"""Render HTML for a single command."""
|
|
913
|
+
html = '<div class="command">'
|
|
914
|
+
html += f"<h3><code>{command.name}</code></h3>"
|
|
915
|
+
html += f"<p>{command.description}</p>"
|
|
916
|
+
html += self._render_html_command_parameters(command.parameters)
|
|
917
|
+
html += "</div>"
|
|
918
|
+
return html
|
|
919
|
+
|
|
920
|
+
def _render_html_command_parameters(self, parameters: list[ParameterInfo]) -> str:
|
|
921
|
+
"""Render HTML for command parameters."""
|
|
922
|
+
if not parameters:
|
|
923
|
+
return ""
|
|
924
|
+
|
|
925
|
+
html = "<h4>Parameters:</h4><ul>"
|
|
926
|
+
for param in parameters:
|
|
927
|
+
html += f'<li class="parameter"><code>--{param.name}</code> ({param.type_hint}): {param.description}</li>'
|
|
928
|
+
html += "</ul>"
|
|
929
|
+
return html
|
|
930
|
+
|
|
931
|
+
def _render_json(self, reference: CommandReference) -> str:
|
|
932
|
+
"""Render reference as JSON."""
|
|
933
|
+
import json
|
|
934
|
+
|
|
935
|
+
data: dict[str, t.Any] = {
|
|
936
|
+
"generated_at": reference.generated_at.isoformat(),
|
|
937
|
+
"version": reference.version,
|
|
938
|
+
"categories": reference.categories,
|
|
939
|
+
"workflows": reference.workflows,
|
|
940
|
+
"commands": self._serialize_commands(reference.commands),
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
return json.dumps(data, indent=2, default=str)
|
|
944
|
+
|
|
945
|
+
def _serialize_commands(self, commands: dict[str, CommandInfo]) -> dict[str, t.Any]:
|
|
946
|
+
"""Serialize commands for JSON output."""
|
|
947
|
+
serialized_commands = {}
|
|
948
|
+
for name, command in commands.items():
|
|
949
|
+
serialized_commands[name] = self._serialize_command(command)
|
|
950
|
+
return serialized_commands
|
|
951
|
+
|
|
952
|
+
def _serialize_command(self, command: CommandInfo) -> dict[str, t.Any]:
|
|
953
|
+
"""Serialize a single command for JSON output."""
|
|
954
|
+
return {
|
|
955
|
+
"name": command.name,
|
|
956
|
+
"description": command.description,
|
|
957
|
+
"category": command.category,
|
|
958
|
+
"parameters": self._serialize_parameters(command.parameters),
|
|
959
|
+
"examples": command.examples,
|
|
960
|
+
"related_commands": command.related_commands,
|
|
961
|
+
"aliases": command.aliases,
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
def _serialize_parameters(
|
|
965
|
+
self, parameters: list[ParameterInfo]
|
|
966
|
+
) -> list[dict[str, t.Any]]:
|
|
967
|
+
"""Serialize parameters for JSON output."""
|
|
968
|
+
return [self._serialize_parameter(param) for param in parameters]
|
|
969
|
+
|
|
970
|
+
def _serialize_parameter(self, param: ParameterInfo) -> dict[str, t.Any]:
|
|
971
|
+
"""Serialize a single parameter for JSON output."""
|
|
972
|
+
return {
|
|
973
|
+
"name": param.name,
|
|
974
|
+
"type": param.type_hint,
|
|
975
|
+
"default": param.default_value,
|
|
976
|
+
"description": param.description,
|
|
977
|
+
"required": param.required,
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
def _render_yaml(self, reference: CommandReference) -> str:
|
|
981
|
+
"""Render reference as YAML."""
|
|
982
|
+
import yaml
|
|
983
|
+
|
|
984
|
+
# Convert to JSON-serializable format first
|
|
985
|
+
json_data = self._render_json(reference)
|
|
986
|
+
import json
|
|
987
|
+
|
|
988
|
+
data = json.loads(json_data)
|
|
989
|
+
|
|
990
|
+
return yaml.dump(data, default_flow_style=False, sort_keys=False)
|
|
991
|
+
|
|
992
|
+
def _render_rst(self, reference: CommandReference) -> str:
|
|
993
|
+
"""Render reference as reStructuredText."""
|
|
994
|
+
lines = [
|
|
995
|
+
"Command Reference",
|
|
996
|
+
"=================",
|
|
997
|
+
"",
|
|
998
|
+
f"Generated: {reference.generated_at.strftime('%Y-%m-%d %H:%M:%S')}",
|
|
999
|
+
f"Version: {reference.version}",
|
|
1000
|
+
"",
|
|
1001
|
+
]
|
|
1002
|
+
|
|
1003
|
+
lines.extend(self._render_rst_categories(reference))
|
|
1004
|
+
return "\n".join(lines)
|
|
1005
|
+
|
|
1006
|
+
def _render_rst_categories(self, reference: CommandReference) -> list[str]:
|
|
1007
|
+
"""Render RST categories and commands."""
|
|
1008
|
+
rst_lines = []
|
|
1009
|
+
|
|
1010
|
+
for category, command_names in reference.categories.items():
|
|
1011
|
+
rst_lines.extend(
|
|
1012
|
+
[
|
|
1013
|
+
category.title(),
|
|
1014
|
+
"-" * len(category),
|
|
1015
|
+
"",
|
|
1016
|
+
]
|
|
1017
|
+
)
|
|
1018
|
+
|
|
1019
|
+
rst_lines.extend(
|
|
1020
|
+
self._render_rst_category_commands(reference.commands, command_names)
|
|
1021
|
+
)
|
|
1022
|
+
|
|
1023
|
+
return rst_lines
|
|
1024
|
+
|
|
1025
|
+
def _render_rst_category_commands(
|
|
1026
|
+
self, commands: dict[str, CommandInfo], command_names: list[str]
|
|
1027
|
+
) -> list[str]:
|
|
1028
|
+
"""Render RST commands for a category."""
|
|
1029
|
+
command_lines = []
|
|
1030
|
+
|
|
1031
|
+
for command_name in command_names:
|
|
1032
|
+
command = commands[command_name]
|
|
1033
|
+
command_lines.extend(
|
|
1034
|
+
[
|
|
1035
|
+
f"``{command.name}``",
|
|
1036
|
+
"^" * (len(command.name) + 4),
|
|
1037
|
+
"",
|
|
1038
|
+
command.description,
|
|
1039
|
+
"",
|
|
1040
|
+
]
|
|
1041
|
+
)
|
|
1042
|
+
|
|
1043
|
+
if command.parameters:
|
|
1044
|
+
command_lines.extend(
|
|
1045
|
+
self._render_rst_command_parameters(command.parameters)
|
|
1046
|
+
)
|
|
1047
|
+
|
|
1048
|
+
return command_lines
|
|
1049
|
+
|
|
1050
|
+
def _render_rst_command_parameters(
|
|
1051
|
+
self, parameters: list[ParameterInfo]
|
|
1052
|
+
) -> list[str]:
|
|
1053
|
+
"""Render RST command parameters."""
|
|
1054
|
+
param_lines = [
|
|
1055
|
+
"Parameters:",
|
|
1056
|
+
"",
|
|
1057
|
+
]
|
|
1058
|
+
|
|
1059
|
+
for param in parameters:
|
|
1060
|
+
param_lines.append(
|
|
1061
|
+
f"* ``--{param.name}`` ({param.type_hint}): {param.description}"
|
|
1062
|
+
)
|
|
1063
|
+
|
|
1064
|
+
param_lines.append("")
|
|
1065
|
+
return param_lines
|