tapps-agents 3.6.0__py3-none-any.whl → 3.6.1__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.
- tapps_agents/__init__.py +2 -2
- tapps_agents/agents/__init__.py +22 -22
- tapps_agents/agents/analyst/__init__.py +5 -5
- tapps_agents/agents/architect/__init__.py +5 -5
- tapps_agents/agents/architect/agent.py +1033 -1033
- tapps_agents/agents/architect/pattern_detector.py +75 -75
- tapps_agents/agents/cleanup/__init__.py +7 -7
- tapps_agents/agents/cleanup/agent.py +445 -445
- tapps_agents/agents/debugger/__init__.py +7 -7
- tapps_agents/agents/debugger/agent.py +310 -310
- tapps_agents/agents/debugger/error_analyzer.py +437 -437
- tapps_agents/agents/designer/__init__.py +5 -5
- tapps_agents/agents/designer/agent.py +786 -786
- tapps_agents/agents/designer/visual_designer.py +638 -638
- tapps_agents/agents/documenter/__init__.py +7 -7
- tapps_agents/agents/documenter/agent.py +531 -531
- tapps_agents/agents/documenter/doc_generator.py +472 -472
- tapps_agents/agents/documenter/doc_validator.py +393 -393
- tapps_agents/agents/documenter/framework_doc_updater.py +493 -493
- tapps_agents/agents/enhancer/__init__.py +7 -7
- tapps_agents/agents/evaluator/__init__.py +7 -7
- tapps_agents/agents/evaluator/agent.py +443 -443
- tapps_agents/agents/evaluator/priority_evaluator.py +641 -641
- tapps_agents/agents/evaluator/quality_analyzer.py +147 -147
- tapps_agents/agents/evaluator/report_generator.py +344 -344
- tapps_agents/agents/evaluator/usage_analyzer.py +192 -192
- tapps_agents/agents/evaluator/workflow_analyzer.py +189 -189
- tapps_agents/agents/implementer/__init__.py +7 -7
- tapps_agents/agents/implementer/agent.py +798 -798
- tapps_agents/agents/implementer/auto_fix.py +1119 -1119
- tapps_agents/agents/implementer/code_generator.py +73 -73
- tapps_agents/agents/improver/__init__.py +1 -1
- tapps_agents/agents/improver/agent.py +753 -753
- tapps_agents/agents/ops/__init__.py +1 -1
- tapps_agents/agents/ops/agent.py +619 -619
- tapps_agents/agents/ops/dependency_analyzer.py +600 -600
- tapps_agents/agents/orchestrator/__init__.py +5 -5
- tapps_agents/agents/orchestrator/agent.py +522 -522
- tapps_agents/agents/planner/__init__.py +7 -7
- tapps_agents/agents/planner/agent.py +1127 -1127
- tapps_agents/agents/reviewer/__init__.py +24 -24
- tapps_agents/agents/reviewer/agent.py +3513 -3513
- tapps_agents/agents/reviewer/aggregator.py +213 -213
- tapps_agents/agents/reviewer/batch_review.py +448 -448
- tapps_agents/agents/reviewer/cache.py +443 -443
- tapps_agents/agents/reviewer/context7_enhancer.py +630 -630
- tapps_agents/agents/reviewer/context_detector.py +203 -203
- tapps_agents/agents/reviewer/docker_compose_validator.py +158 -158
- tapps_agents/agents/reviewer/dockerfile_validator.py +176 -176
- tapps_agents/agents/reviewer/error_handling.py +126 -126
- tapps_agents/agents/reviewer/feedback_generator.py +490 -490
- tapps_agents/agents/reviewer/influxdb_validator.py +316 -316
- tapps_agents/agents/reviewer/issue_tracking.py +169 -169
- tapps_agents/agents/reviewer/library_detector.py +295 -295
- tapps_agents/agents/reviewer/library_patterns.py +268 -268
- tapps_agents/agents/reviewer/maintainability_scorer.py +593 -593
- tapps_agents/agents/reviewer/metric_strategies.py +276 -276
- tapps_agents/agents/reviewer/mqtt_validator.py +160 -160
- tapps_agents/agents/reviewer/output_enhancer.py +105 -105
- tapps_agents/agents/reviewer/pattern_detector.py +241 -241
- tapps_agents/agents/reviewer/performance_scorer.py +357 -357
- tapps_agents/agents/reviewer/phased_review.py +516 -516
- tapps_agents/agents/reviewer/progressive_review.py +435 -435
- tapps_agents/agents/reviewer/react_scorer.py +331 -331
- tapps_agents/agents/reviewer/score_constants.py +228 -228
- tapps_agents/agents/reviewer/score_validator.py +507 -507
- tapps_agents/agents/reviewer/scorer_registry.py +373 -373
- tapps_agents/agents/reviewer/service_discovery.py +534 -534
- tapps_agents/agents/reviewer/tools/parallel_executor.py +581 -581
- tapps_agents/agents/reviewer/tools/ruff_grouping.py +250 -250
- tapps_agents/agents/reviewer/tools/scoped_mypy.py +284 -284
- tapps_agents/agents/reviewer/typescript_scorer.py +1142 -1142
- tapps_agents/agents/reviewer/validation.py +208 -208
- tapps_agents/agents/reviewer/websocket_validator.py +132 -132
- tapps_agents/agents/tester/__init__.py +7 -7
- tapps_agents/agents/tester/accessibility_auditor.py +309 -309
- tapps_agents/agents/tester/agent.py +1080 -1080
- tapps_agents/agents/tester/batch_generator.py +54 -54
- tapps_agents/agents/tester/context_learner.py +51 -51
- tapps_agents/agents/tester/coverage_analyzer.py +386 -386
- tapps_agents/agents/tester/coverage_test_generator.py +290 -290
- tapps_agents/agents/tester/debug_enhancer.py +238 -238
- tapps_agents/agents/tester/device_emulator.py +241 -241
- tapps_agents/agents/tester/integration_generator.py +62 -62
- tapps_agents/agents/tester/network_recorder.py +300 -300
- tapps_agents/agents/tester/performance_monitor.py +320 -320
- tapps_agents/agents/tester/test_fixer.py +316 -316
- tapps_agents/agents/tester/test_generator.py +632 -632
- tapps_agents/agents/tester/trace_manager.py +234 -234
- tapps_agents/agents/tester/visual_regression.py +291 -291
- tapps_agents/analysis/pattern_detector.py +36 -36
- tapps_agents/beads/hydration.py +213 -213
- tapps_agents/beads/parse.py +32 -32
- tapps_agents/beads/specs.py +206 -206
- tapps_agents/cli/__init__.py +9 -9
- tapps_agents/cli/__main__.py +8 -8
- tapps_agents/cli/base.py +478 -478
- tapps_agents/cli/command_classifier.py +72 -72
- tapps_agents/cli/commands/__init__.py +2 -2
- tapps_agents/cli/commands/analyst.py +173 -173
- tapps_agents/cli/commands/architect.py +109 -109
- tapps_agents/cli/commands/cleanup_agent.py +92 -92
- tapps_agents/cli/commands/common.py +126 -126
- tapps_agents/cli/commands/debugger.py +90 -90
- tapps_agents/cli/commands/designer.py +112 -112
- tapps_agents/cli/commands/documenter.py +136 -136
- tapps_agents/cli/commands/enhancer.py +110 -110
- tapps_agents/cli/commands/evaluator.py +255 -255
- tapps_agents/cli/commands/implementer.py +301 -301
- tapps_agents/cli/commands/improver.py +91 -91
- tapps_agents/cli/commands/knowledge.py +111 -111
- tapps_agents/cli/commands/learning.py +172 -172
- tapps_agents/cli/commands/observability.py +283 -283
- tapps_agents/cli/commands/ops.py +135 -135
- tapps_agents/cli/commands/orchestrator.py +116 -116
- tapps_agents/cli/commands/planner.py +237 -237
- tapps_agents/cli/commands/reviewer.py +1872 -1872
- tapps_agents/cli/commands/status.py +285 -285
- tapps_agents/cli/commands/task.py +227 -227
- tapps_agents/cli/commands/tester.py +191 -191
- tapps_agents/cli/feedback.py +936 -936
- tapps_agents/cli/formatters.py +608 -608
- tapps_agents/cli/help/__init__.py +7 -7
- tapps_agents/cli/help/static_help.py +425 -425
- tapps_agents/cli/network_detection.py +110 -110
- tapps_agents/cli/output_compactor.py +274 -274
- tapps_agents/cli/parsers/__init__.py +2 -2
- tapps_agents/cli/parsers/analyst.py +186 -186
- tapps_agents/cli/parsers/architect.py +167 -167
- tapps_agents/cli/parsers/cleanup_agent.py +228 -228
- tapps_agents/cli/parsers/debugger.py +116 -116
- tapps_agents/cli/parsers/designer.py +182 -182
- tapps_agents/cli/parsers/documenter.py +134 -134
- tapps_agents/cli/parsers/enhancer.py +113 -113
- tapps_agents/cli/parsers/evaluator.py +213 -213
- tapps_agents/cli/parsers/implementer.py +168 -168
- tapps_agents/cli/parsers/improver.py +132 -132
- tapps_agents/cli/parsers/ops.py +159 -159
- tapps_agents/cli/parsers/orchestrator.py +98 -98
- tapps_agents/cli/parsers/planner.py +145 -145
- tapps_agents/cli/parsers/reviewer.py +462 -462
- tapps_agents/cli/parsers/tester.py +124 -124
- tapps_agents/cli/progress_heartbeat.py +254 -254
- tapps_agents/cli/streaming_progress.py +336 -336
- tapps_agents/cli/utils/__init__.py +6 -6
- tapps_agents/cli/utils/agent_lifecycle.py +48 -48
- tapps_agents/cli/utils/error_formatter.py +82 -82
- tapps_agents/cli/utils/error_recovery.py +188 -188
- tapps_agents/cli/utils/output_handler.py +59 -59
- tapps_agents/cli/utils/prompt_enhancer.py +319 -319
- tapps_agents/cli/validators/__init__.py +9 -9
- tapps_agents/cli/validators/command_validator.py +81 -81
- tapps_agents/context7/__init__.py +112 -112
- tapps_agents/context7/agent_integration.py +869 -869
- tapps_agents/context7/analytics.py +382 -382
- tapps_agents/context7/analytics_dashboard.py +299 -299
- tapps_agents/context7/async_cache.py +681 -681
- tapps_agents/context7/backup_client.py +958 -958
- tapps_agents/context7/cache_locking.py +194 -194
- tapps_agents/context7/cache_metadata.py +214 -214
- tapps_agents/context7/cache_prewarm.py +488 -488
- tapps_agents/context7/cache_structure.py +168 -168
- tapps_agents/context7/cache_warming.py +604 -604
- tapps_agents/context7/circuit_breaker.py +376 -376
- tapps_agents/context7/cleanup.py +461 -461
- tapps_agents/context7/commands.py +858 -858
- tapps_agents/context7/credential_validation.py +276 -276
- tapps_agents/context7/cross_reference_resolver.py +168 -168
- tapps_agents/context7/cross_references.py +424 -424
- tapps_agents/context7/doc_manager.py +225 -225
- tapps_agents/context7/fuzzy_matcher.py +369 -369
- tapps_agents/context7/kb_cache.py +404 -404
- tapps_agents/context7/language_detector.py +219 -219
- tapps_agents/context7/library_detector.py +725 -725
- tapps_agents/context7/lookup.py +738 -738
- tapps_agents/context7/metadata.py +258 -258
- tapps_agents/context7/refresh_queue.py +300 -300
- tapps_agents/context7/security.py +373 -373
- tapps_agents/context7/staleness_policies.py +278 -278
- tapps_agents/context7/tiles_integration.py +47 -47
- tapps_agents/continuous_bug_fix/__init__.py +20 -20
- tapps_agents/continuous_bug_fix/bug_finder.py +306 -306
- tapps_agents/continuous_bug_fix/bug_fix_coordinator.py +177 -177
- tapps_agents/continuous_bug_fix/commit_manager.py +178 -178
- tapps_agents/continuous_bug_fix/continuous_bug_fixer.py +322 -322
- tapps_agents/continuous_bug_fix/proactive_bug_finder.py +285 -285
- tapps_agents/core/__init__.py +298 -298
- tapps_agents/core/adaptive_cache_config.py +432 -432
- tapps_agents/core/agent_base.py +647 -647
- tapps_agents/core/agent_cache.py +466 -466
- tapps_agents/core/agent_learning.py +1865 -1865
- tapps_agents/core/analytics_dashboard.py +563 -563
- tapps_agents/core/analytics_enhancements.py +597 -597
- tapps_agents/core/anonymization.py +274 -274
- tapps_agents/core/ast_parser.py +228 -228
- tapps_agents/core/async_file_ops.py +402 -402
- tapps_agents/core/best_practice_consultant.py +299 -299
- tapps_agents/core/brownfield_analyzer.py +299 -299
- tapps_agents/core/brownfield_review.py +541 -541
- tapps_agents/core/browser_controller.py +513 -513
- tapps_agents/core/capability_registry.py +418 -418
- tapps_agents/core/change_impact_analyzer.py +190 -190
- tapps_agents/core/checkpoint_manager.py +377 -377
- tapps_agents/core/code_generator.py +329 -329
- tapps_agents/core/code_validator.py +276 -276
- tapps_agents/core/command_registry.py +327 -327
- tapps_agents/core/context_gathering/__init__.py +2 -2
- tapps_agents/core/context_gathering/repository_explorer.py +28 -28
- tapps_agents/core/context_intelligence/__init__.py +2 -2
- tapps_agents/core/context_intelligence/relevance_scorer.py +24 -24
- tapps_agents/core/context_intelligence/token_budget_manager.py +27 -27
- tapps_agents/core/context_manager.py +240 -240
- tapps_agents/core/cursor_feedback_monitor.py +146 -146
- tapps_agents/core/cursor_verification.py +290 -290
- tapps_agents/core/customization_loader.py +280 -280
- tapps_agents/core/customization_schema.py +260 -260
- tapps_agents/core/customization_template.py +238 -238
- tapps_agents/core/debug_logger.py +124 -124
- tapps_agents/core/design_validator.py +298 -298
- tapps_agents/core/diagram_generator.py +226 -226
- tapps_agents/core/docker_utils.py +232 -232
- tapps_agents/core/document_generator.py +617 -617
- tapps_agents/core/domain_detector.py +30 -30
- tapps_agents/core/error_envelope.py +454 -454
- tapps_agents/core/error_handler.py +270 -270
- tapps_agents/core/estimation_tracker.py +189 -189
- tapps_agents/core/eval_prompt_engine.py +116 -116
- tapps_agents/core/evaluation_base.py +119 -119
- tapps_agents/core/evaluation_models.py +320 -320
- tapps_agents/core/evaluation_orchestrator.py +225 -225
- tapps_agents/core/evaluators/__init__.py +7 -7
- tapps_agents/core/evaluators/architectural_evaluator.py +205 -205
- tapps_agents/core/evaluators/behavioral_evaluator.py +160 -160
- tapps_agents/core/evaluators/performance_profile_evaluator.py +160 -160
- tapps_agents/core/evaluators/security_posture_evaluator.py +148 -148
- tapps_agents/core/evaluators/spec_compliance_evaluator.py +181 -181
- tapps_agents/core/exceptions.py +107 -107
- tapps_agents/core/expert_config_generator.py +293 -293
- tapps_agents/core/export_schema.py +202 -202
- tapps_agents/core/external_feedback_models.py +102 -102
- tapps_agents/core/external_feedback_storage.py +213 -213
- tapps_agents/core/fallback_strategy.py +314 -314
- tapps_agents/core/feedback_analyzer.py +162 -162
- tapps_agents/core/feedback_collector.py +178 -178
- tapps_agents/core/git_operations.py +445 -445
- tapps_agents/core/hardware_profiler.py +151 -151
- tapps_agents/core/instructions.py +324 -324
- tapps_agents/core/io_guardrails.py +69 -69
- tapps_agents/core/issue_manifest.py +249 -249
- tapps_agents/core/issue_schema.py +139 -139
- tapps_agents/core/json_utils.py +128 -128
- tapps_agents/core/knowledge_graph.py +446 -446
- tapps_agents/core/language_detector.py +296 -296
- tapps_agents/core/learning_confidence.py +242 -242
- tapps_agents/core/learning_dashboard.py +246 -246
- tapps_agents/core/learning_decision.py +384 -384
- tapps_agents/core/learning_explainability.py +578 -578
- tapps_agents/core/learning_export.py +287 -287
- tapps_agents/core/learning_integration.py +228 -228
- tapps_agents/core/llm_behavior.py +232 -232
- tapps_agents/core/long_duration_support.py +786 -786
- tapps_agents/core/mcp_setup.py +106 -106
- tapps_agents/core/memory_integration.py +396 -396
- tapps_agents/core/meta_learning.py +666 -666
- tapps_agents/core/module_path_sanitizer.py +199 -199
- tapps_agents/core/multi_agent_orchestrator.py +382 -382
- tapps_agents/core/network_errors.py +125 -125
- tapps_agents/core/nfr_validator.py +336 -336
- tapps_agents/core/offline_mode.py +158 -158
- tapps_agents/core/output_contracts.py +300 -300
- tapps_agents/core/output_formatter.py +300 -300
- tapps_agents/core/path_normalizer.py +174 -174
- tapps_agents/core/path_validator.py +322 -322
- tapps_agents/core/pattern_library.py +250 -250
- tapps_agents/core/performance_benchmark.py +301 -301
- tapps_agents/core/performance_monitor.py +184 -184
- tapps_agents/core/playwright_mcp_controller.py +771 -771
- tapps_agents/core/policy_loader.py +135 -135
- tapps_agents/core/progress.py +166 -166
- tapps_agents/core/project_profile.py +354 -354
- tapps_agents/core/project_type_detector.py +454 -454
- tapps_agents/core/prompt_base.py +223 -223
- tapps_agents/core/prompt_learning/__init__.py +2 -2
- tapps_agents/core/prompt_learning/learning_loop.py +24 -24
- tapps_agents/core/prompt_learning/project_prompt_store.py +25 -25
- tapps_agents/core/prompt_learning/skills_prompt_analyzer.py +35 -35
- tapps_agents/core/prompt_optimization/__init__.py +6 -6
- tapps_agents/core/prompt_optimization/ab_tester.py +114 -114
- tapps_agents/core/prompt_optimization/correlation_analyzer.py +160 -160
- tapps_agents/core/prompt_optimization/progressive_refiner.py +129 -129
- tapps_agents/core/prompt_optimization/prompt_library.py +37 -37
- tapps_agents/core/requirements_evaluator.py +431 -431
- tapps_agents/core/resource_aware_executor.py +449 -449
- tapps_agents/core/resource_monitor.py +343 -343
- tapps_agents/core/resume_handler.py +298 -298
- tapps_agents/core/retry_handler.py +197 -197
- tapps_agents/core/review_checklists.py +479 -479
- tapps_agents/core/role_loader.py +201 -201
- tapps_agents/core/role_template_loader.py +201 -201
- tapps_agents/core/runtime_mode.py +60 -60
- tapps_agents/core/security_scanner.py +342 -342
- tapps_agents/core/skill_agent_registry.py +194 -194
- tapps_agents/core/skill_integration.py +208 -208
- tapps_agents/core/skill_loader.py +492 -492
- tapps_agents/core/skill_template.py +341 -341
- tapps_agents/core/skill_validator.py +478 -478
- tapps_agents/core/stack_analyzer.py +35 -35
- tapps_agents/core/startup.py +174 -174
- tapps_agents/core/storage_manager.py +397 -397
- tapps_agents/core/storage_models.py +166 -166
- tapps_agents/core/story_evaluator.py +410 -410
- tapps_agents/core/subprocess_utils.py +170 -170
- tapps_agents/core/task_duration.py +296 -296
- tapps_agents/core/task_memory.py +582 -582
- tapps_agents/core/task_state.py +226 -226
- tapps_agents/core/tech_stack_priorities.py +208 -208
- tapps_agents/core/temp_directory.py +194 -194
- tapps_agents/core/template_merger.py +600 -600
- tapps_agents/core/template_selector.py +280 -280
- tapps_agents/core/test_generator.py +286 -286
- tapps_agents/core/tiered_context.py +253 -253
- tapps_agents/core/token_monitor.py +345 -345
- tapps_agents/core/traceability.py +254 -254
- tapps_agents/core/trajectory_tracker.py +50 -50
- tapps_agents/core/unicode_safe.py +143 -143
- tapps_agents/core/unified_cache_config.py +170 -170
- tapps_agents/core/unified_state.py +324 -324
- tapps_agents/core/validate_cursor_setup.py +237 -237
- tapps_agents/core/validation_registry.py +136 -136
- tapps_agents/core/validators/__init__.py +4 -4
- tapps_agents/core/validators/python_validator.py +87 -87
- tapps_agents/core/verification_agent.py +90 -90
- tapps_agents/core/visual_feedback.py +644 -644
- tapps_agents/core/workflow_validator.py +197 -197
- tapps_agents/core/worktree.py +367 -367
- tapps_agents/docker/__init__.py +10 -10
- tapps_agents/docker/analyzer.py +186 -186
- tapps_agents/docker/debugger.py +229 -229
- tapps_agents/docker/error_patterns.py +216 -216
- tapps_agents/epic/__init__.py +22 -22
- tapps_agents/epic/beads_sync.py +115 -115
- tapps_agents/epic/markdown_sync.py +105 -105
- tapps_agents/epic/models.py +96 -96
- tapps_agents/experts/__init__.py +163 -163
- tapps_agents/experts/agent_integration.py +243 -243
- tapps_agents/experts/auto_generator.py +331 -331
- tapps_agents/experts/base_expert.py +536 -536
- tapps_agents/experts/builtin_registry.py +261 -261
- tapps_agents/experts/business_metrics.py +565 -565
- tapps_agents/experts/cache.py +266 -266
- tapps_agents/experts/confidence_breakdown.py +306 -306
- tapps_agents/experts/confidence_calculator.py +336 -336
- tapps_agents/experts/confidence_metrics.py +236 -236
- tapps_agents/experts/domain_config.py +311 -311
- tapps_agents/experts/domain_detector.py +550 -550
- tapps_agents/experts/domain_utils.py +84 -84
- tapps_agents/experts/expert_config.py +113 -113
- tapps_agents/experts/expert_engine.py +465 -465
- tapps_agents/experts/expert_registry.py +744 -744
- tapps_agents/experts/expert_synthesizer.py +70 -70
- tapps_agents/experts/governance.py +197 -197
- tapps_agents/experts/history_logger.py +312 -312
- tapps_agents/experts/knowledge/README.md +180 -180
- tapps_agents/experts/knowledge/accessibility/accessible-forms.md +331 -331
- tapps_agents/experts/knowledge/accessibility/aria-patterns.md +344 -344
- tapps_agents/experts/knowledge/accessibility/color-contrast.md +285 -285
- tapps_agents/experts/knowledge/accessibility/keyboard-navigation.md +332 -332
- tapps_agents/experts/knowledge/accessibility/screen-readers.md +282 -282
- tapps_agents/experts/knowledge/accessibility/semantic-html.md +355 -355
- tapps_agents/experts/knowledge/accessibility/testing-accessibility.md +369 -369
- tapps_agents/experts/knowledge/accessibility/wcag-2.1.md +296 -296
- tapps_agents/experts/knowledge/accessibility/wcag-2.2.md +211 -211
- tapps_agents/experts/knowledge/agent-learning/best-practices.md +715 -715
- tapps_agents/experts/knowledge/agent-learning/pattern-extraction.md +282 -282
- tapps_agents/experts/knowledge/agent-learning/prompt-optimization.md +320 -320
- tapps_agents/experts/knowledge/ai-frameworks/model-optimization.md +90 -90
- tapps_agents/experts/knowledge/ai-frameworks/openvino-patterns.md +260 -260
- tapps_agents/experts/knowledge/api-design-integration/api-gateway-patterns.md +309 -309
- tapps_agents/experts/knowledge/api-design-integration/api-security-patterns.md +521 -521
- tapps_agents/experts/knowledge/api-design-integration/api-versioning.md +421 -421
- tapps_agents/experts/knowledge/api-design-integration/async-protocol-patterns.md +61 -61
- tapps_agents/experts/knowledge/api-design-integration/contract-testing.md +221 -221
- tapps_agents/experts/knowledge/api-design-integration/external-api-integration.md +489 -489
- tapps_agents/experts/knowledge/api-design-integration/fastapi-patterns.md +360 -360
- tapps_agents/experts/knowledge/api-design-integration/fastapi-testing.md +262 -262
- tapps_agents/experts/knowledge/api-design-integration/graphql-patterns.md +582 -582
- tapps_agents/experts/knowledge/api-design-integration/grpc-best-practices.md +499 -499
- tapps_agents/experts/knowledge/api-design-integration/mqtt-patterns.md +455 -455
- tapps_agents/experts/knowledge/api-design-integration/rate-limiting.md +507 -507
- tapps_agents/experts/knowledge/api-design-integration/restful-api-design.md +618 -618
- tapps_agents/experts/knowledge/api-design-integration/websocket-patterns.md +480 -480
- tapps_agents/experts/knowledge/cloud-infrastructure/cloud-native-patterns.md +175 -175
- tapps_agents/experts/knowledge/cloud-infrastructure/container-health-checks.md +261 -261
- tapps_agents/experts/knowledge/cloud-infrastructure/containerization.md +222 -222
- tapps_agents/experts/knowledge/cloud-infrastructure/cost-optimization.md +122 -122
- tapps_agents/experts/knowledge/cloud-infrastructure/disaster-recovery.md +153 -153
- tapps_agents/experts/knowledge/cloud-infrastructure/dockerfile-patterns.md +285 -285
- tapps_agents/experts/knowledge/cloud-infrastructure/infrastructure-as-code.md +187 -187
- tapps_agents/experts/knowledge/cloud-infrastructure/kubernetes-patterns.md +253 -253
- tapps_agents/experts/knowledge/cloud-infrastructure/multi-cloud-strategies.md +155 -155
- tapps_agents/experts/knowledge/cloud-infrastructure/serverless-architecture.md +200 -200
- tapps_agents/experts/knowledge/code-quality-analysis/README.md +16 -16
- tapps_agents/experts/knowledge/code-quality-analysis/code-metrics.md +137 -137
- tapps_agents/experts/knowledge/code-quality-analysis/complexity-analysis.md +181 -181
- tapps_agents/experts/knowledge/code-quality-analysis/technical-debt-patterns.md +191 -191
- tapps_agents/experts/knowledge/data-privacy-compliance/anonymization.md +313 -313
- tapps_agents/experts/knowledge/data-privacy-compliance/ccpa.md +255 -255
- tapps_agents/experts/knowledge/data-privacy-compliance/consent-management.md +282 -282
- tapps_agents/experts/knowledge/data-privacy-compliance/data-minimization.md +275 -275
- tapps_agents/experts/knowledge/data-privacy-compliance/data-retention.md +297 -297
- tapps_agents/experts/knowledge/data-privacy-compliance/data-subject-rights.md +383 -383
- tapps_agents/experts/knowledge/data-privacy-compliance/encryption-privacy.md +285 -285
- tapps_agents/experts/knowledge/data-privacy-compliance/gdpr.md +344 -344
- tapps_agents/experts/knowledge/data-privacy-compliance/hipaa.md +385 -385
- tapps_agents/experts/knowledge/data-privacy-compliance/privacy-by-design.md +280 -280
- tapps_agents/experts/knowledge/database-data-management/acid-vs-cap.md +164 -164
- tapps_agents/experts/knowledge/database-data-management/backup-and-recovery.md +182 -182
- tapps_agents/experts/knowledge/database-data-management/data-modeling.md +172 -172
- tapps_agents/experts/knowledge/database-data-management/database-design.md +187 -187
- tapps_agents/experts/knowledge/database-data-management/flux-query-optimization.md +342 -342
- tapps_agents/experts/knowledge/database-data-management/influxdb-connection-patterns.md +432 -432
- tapps_agents/experts/knowledge/database-data-management/influxdb-patterns.md +442 -442
- tapps_agents/experts/knowledge/database-data-management/migration-strategies.md +216 -216
- tapps_agents/experts/knowledge/database-data-management/nosql-patterns.md +259 -259
- tapps_agents/experts/knowledge/database-data-management/scalability-patterns.md +184 -184
- tapps_agents/experts/knowledge/database-data-management/sql-optimization.md +175 -175
- tapps_agents/experts/knowledge/database-data-management/time-series-modeling.md +444 -444
- tapps_agents/experts/knowledge/development-workflow/README.md +16 -16
- tapps_agents/experts/knowledge/development-workflow/automation-best-practices.md +216 -216
- tapps_agents/experts/knowledge/development-workflow/build-strategies.md +198 -198
- tapps_agents/experts/knowledge/development-workflow/deployment-patterns.md +205 -205
- tapps_agents/experts/knowledge/development-workflow/git-workflows.md +205 -205
- tapps_agents/experts/knowledge/documentation-knowledge-management/README.md +16 -16
- tapps_agents/experts/knowledge/documentation-knowledge-management/api-documentation-patterns.md +231 -231
- tapps_agents/experts/knowledge/documentation-knowledge-management/documentation-standards.md +191 -191
- tapps_agents/experts/knowledge/documentation-knowledge-management/knowledge-management.md +171 -171
- tapps_agents/experts/knowledge/documentation-knowledge-management/technical-writing-guide.md +192 -192
- tapps_agents/experts/knowledge/observability-monitoring/alerting-patterns.md +461 -461
- tapps_agents/experts/knowledge/observability-monitoring/apm-tools.md +459 -459
- tapps_agents/experts/knowledge/observability-monitoring/distributed-tracing.md +367 -367
- tapps_agents/experts/knowledge/observability-monitoring/logging-strategies.md +478 -478
- tapps_agents/experts/knowledge/observability-monitoring/metrics-and-monitoring.md +510 -510
- tapps_agents/experts/knowledge/observability-monitoring/observability-best-practices.md +492 -492
- tapps_agents/experts/knowledge/observability-monitoring/open-telemetry.md +573 -573
- tapps_agents/experts/knowledge/observability-monitoring/slo-sli-sla.md +419 -419
- tapps_agents/experts/knowledge/performance/anti-patterns.md +284 -284
- tapps_agents/experts/knowledge/performance/api-performance.md +256 -256
- tapps_agents/experts/knowledge/performance/caching.md +327 -327
- tapps_agents/experts/knowledge/performance/database-performance.md +252 -252
- tapps_agents/experts/knowledge/performance/optimization-patterns.md +327 -327
- tapps_agents/experts/knowledge/performance/profiling.md +297 -297
- tapps_agents/experts/knowledge/performance/resource-management.md +293 -293
- tapps_agents/experts/knowledge/performance/scalability.md +306 -306
- tapps_agents/experts/knowledge/security/owasp-top10.md +209 -209
- tapps_agents/experts/knowledge/security/secure-coding-practices.md +207 -207
- tapps_agents/experts/knowledge/security/threat-modeling.md +220 -220
- tapps_agents/experts/knowledge/security/vulnerability-patterns.md +342 -342
- tapps_agents/experts/knowledge/software-architecture/docker-compose-patterns.md +314 -314
- tapps_agents/experts/knowledge/software-architecture/microservices-patterns.md +379 -379
- tapps_agents/experts/knowledge/software-architecture/service-communication.md +316 -316
- tapps_agents/experts/knowledge/testing/best-practices.md +310 -310
- tapps_agents/experts/knowledge/testing/coverage-analysis.md +293 -293
- tapps_agents/experts/knowledge/testing/mocking.md +256 -256
- tapps_agents/experts/knowledge/testing/test-automation.md +276 -276
- tapps_agents/experts/knowledge/testing/test-data.md +271 -271
- tapps_agents/experts/knowledge/testing/test-design-patterns.md +280 -280
- tapps_agents/experts/knowledge/testing/test-maintenance.md +236 -236
- tapps_agents/experts/knowledge/testing/test-strategies.md +311 -311
- tapps_agents/experts/knowledge/user-experience/information-architecture.md +325 -325
- tapps_agents/experts/knowledge/user-experience/interaction-design.md +363 -363
- tapps_agents/experts/knowledge/user-experience/prototyping.md +293 -293
- tapps_agents/experts/knowledge/user-experience/usability-heuristics.md +337 -337
- tapps_agents/experts/knowledge/user-experience/usability-testing.md +311 -311
- tapps_agents/experts/knowledge/user-experience/user-journeys.md +296 -296
- tapps_agents/experts/knowledge/user-experience/user-research.md +373 -373
- tapps_agents/experts/knowledge/user-experience/ux-principles.md +340 -340
- tapps_agents/experts/knowledge_freshness.py +321 -321
- tapps_agents/experts/knowledge_ingestion.py +438 -438
- tapps_agents/experts/knowledge_need_detector.py +93 -93
- tapps_agents/experts/knowledge_validator.py +382 -382
- tapps_agents/experts/observability.py +440 -440
- tapps_agents/experts/passive_notifier.py +238 -238
- tapps_agents/experts/proactive_orchestrator.py +32 -32
- tapps_agents/experts/rag_chunker.py +205 -205
- tapps_agents/experts/rag_embedder.py +152 -152
- tapps_agents/experts/rag_evaluation.py +299 -299
- tapps_agents/experts/rag_index.py +303 -303
- tapps_agents/experts/rag_metrics.py +293 -293
- tapps_agents/experts/rag_safety.py +263 -263
- tapps_agents/experts/report_generator.py +296 -296
- tapps_agents/experts/setup_wizard.py +441 -441
- tapps_agents/experts/simple_rag.py +431 -431
- tapps_agents/experts/vector_rag.py +354 -354
- tapps_agents/experts/weight_distributor.py +304 -304
- tapps_agents/health/__init__.py +24 -24
- tapps_agents/health/base.py +75 -75
- tapps_agents/health/checks/__init__.py +22 -22
- tapps_agents/health/checks/automation.py +127 -127
- tapps_agents/health/checks/context7_cache.py +210 -210
- tapps_agents/health/checks/environment.py +116 -116
- tapps_agents/health/checks/execution.py +170 -170
- tapps_agents/health/checks/knowledge_base.py +187 -187
- tapps_agents/health/checks/outcomes.backup_20260204_064058.py +324 -0
- tapps_agents/health/checks/outcomes.backup_20260204_064256.py +324 -0
- tapps_agents/health/checks/outcomes.backup_20260204_064600.py +324 -0
- tapps_agents/health/checks/outcomes.py +324 -324
- tapps_agents/health/collector.py +280 -280
- tapps_agents/health/dashboard.py +137 -137
- tapps_agents/health/metrics.py +151 -151
- tapps_agents/health/registry.py +166 -166
- tapps_agents/hooks/__init__.py +33 -33
- tapps_agents/hooks/config.py +140 -140
- tapps_agents/hooks/events.py +135 -135
- tapps_agents/hooks/executor.py +128 -128
- tapps_agents/hooks/manager.py +143 -143
- tapps_agents/integration/__init__.py +8 -8
- tapps_agents/integration/service_integrator.py +121 -121
- tapps_agents/integrations/__init__.py +10 -10
- tapps_agents/integrations/clawdbot.py +525 -525
- tapps_agents/integrations/memory_bridge.py +356 -356
- tapps_agents/mcp/__init__.py +18 -18
- tapps_agents/mcp/gateway.py +112 -112
- tapps_agents/mcp/servers/__init__.py +13 -13
- tapps_agents/mcp/servers/analysis.py +204 -204
- tapps_agents/mcp/servers/context7.py +198 -198
- tapps_agents/mcp/servers/filesystem.py +218 -218
- tapps_agents/mcp/servers/git.py +201 -201
- tapps_agents/mcp/tool_registry.py +115 -115
- tapps_agents/quality/__init__.py +54 -54
- tapps_agents/quality/coverage_analyzer.py +379 -379
- tapps_agents/quality/enforcement.py +82 -82
- tapps_agents/quality/gates/__init__.py +37 -37
- tapps_agents/quality/gates/approval_gate.py +255 -255
- tapps_agents/quality/gates/base.py +84 -84
- tapps_agents/quality/gates/exceptions.py +43 -43
- tapps_agents/quality/gates/policy_gate.py +195 -195
- tapps_agents/quality/gates/registry.py +239 -239
- tapps_agents/quality/gates/security_gate.py +156 -156
- tapps_agents/quality/quality_gates.py +369 -369
- tapps_agents/quality/secret_scanner.py +335 -335
- tapps_agents/resources/__init__.py +5 -0
- tapps_agents/resources/claude/__init__.py +1 -0
- tapps_agents/resources/claude/commands/README.md +156 -0
- tapps_agents/resources/claude/commands/__init__.py +1 -0
- tapps_agents/resources/claude/commands/build-fix.md +22 -0
- tapps_agents/resources/claude/commands/build.md +77 -0
- tapps_agents/resources/claude/commands/debug.md +53 -0
- tapps_agents/resources/claude/commands/design.md +68 -0
- tapps_agents/resources/claude/commands/docs.md +53 -0
- tapps_agents/resources/claude/commands/e2e.md +22 -0
- tapps_agents/resources/claude/commands/fix.md +54 -0
- tapps_agents/resources/claude/commands/implement.md +53 -0
- tapps_agents/resources/claude/commands/improve.md +53 -0
- tapps_agents/resources/claude/commands/library-docs.md +64 -0
- tapps_agents/resources/claude/commands/lint.md +52 -0
- tapps_agents/resources/claude/commands/plan.md +65 -0
- tapps_agents/resources/claude/commands/refactor-clean.md +21 -0
- tapps_agents/resources/claude/commands/refactor.md +55 -0
- tapps_agents/resources/claude/commands/review.md +67 -0
- tapps_agents/resources/claude/commands/score.md +60 -0
- tapps_agents/resources/claude/commands/security-review.md +22 -0
- tapps_agents/resources/claude/commands/security-scan.md +54 -0
- tapps_agents/resources/claude/commands/tdd.md +24 -0
- tapps_agents/resources/claude/commands/test-coverage.md +21 -0
- tapps_agents/resources/claude/commands/test.md +54 -0
- tapps_agents/resources/claude/commands/update-codemaps.md +20 -0
- tapps_agents/resources/claude/commands/update-docs.md +21 -0
- tapps_agents/resources/claude/skills/__init__.py +1 -0
- tapps_agents/resources/claude/skills/analyst/SKILL.md +272 -0
- tapps_agents/resources/claude/skills/analyst/__init__.py +1 -0
- tapps_agents/resources/claude/skills/architect/SKILL.md +282 -0
- tapps_agents/resources/claude/skills/architect/__init__.py +1 -0
- tapps_agents/resources/claude/skills/backend-patterns/SKILL.md +30 -0
- tapps_agents/resources/claude/skills/backend-patterns/__init__.py +1 -0
- tapps_agents/resources/claude/skills/coding-standards/SKILL.md +29 -0
- tapps_agents/resources/claude/skills/coding-standards/__init__.py +1 -0
- tapps_agents/resources/claude/skills/debugger/SKILL.md +203 -0
- tapps_agents/resources/claude/skills/debugger/__init__.py +1 -0
- tapps_agents/resources/claude/skills/designer/SKILL.md +243 -0
- tapps_agents/resources/claude/skills/designer/__init__.py +1 -0
- tapps_agents/resources/claude/skills/documenter/SKILL.md +252 -0
- tapps_agents/resources/claude/skills/documenter/__init__.py +1 -0
- tapps_agents/resources/claude/skills/enhancer/SKILL.md +307 -0
- tapps_agents/resources/claude/skills/enhancer/__init__.py +1 -0
- tapps_agents/resources/claude/skills/evaluator/SKILL.md +204 -0
- tapps_agents/resources/claude/skills/evaluator/__init__.py +1 -0
- tapps_agents/resources/claude/skills/frontend-patterns/SKILL.md +29 -0
- tapps_agents/resources/claude/skills/frontend-patterns/__init__.py +1 -0
- tapps_agents/resources/claude/skills/implementer/SKILL.md +188 -0
- tapps_agents/resources/claude/skills/implementer/__init__.py +1 -0
- tapps_agents/resources/claude/skills/improver/SKILL.md +218 -0
- tapps_agents/resources/claude/skills/improver/__init__.py +1 -0
- tapps_agents/resources/claude/skills/ops/SKILL.md +281 -0
- tapps_agents/resources/claude/skills/ops/__init__.py +1 -0
- tapps_agents/resources/claude/skills/orchestrator/SKILL.md +390 -0
- tapps_agents/resources/claude/skills/orchestrator/__init__.py +1 -0
- tapps_agents/resources/claude/skills/planner/SKILL.md +254 -0
- tapps_agents/resources/claude/skills/planner/__init__.py +1 -0
- tapps_agents/resources/claude/skills/reviewer/SKILL.md +434 -0
- tapps_agents/resources/claude/skills/reviewer/__init__.py +1 -0
- tapps_agents/resources/claude/skills/security-review/SKILL.md +31 -0
- tapps_agents/resources/claude/skills/security-review/__init__.py +1 -0
- tapps_agents/resources/claude/skills/simple-mode/SKILL.md +695 -0
- tapps_agents/resources/claude/skills/simple-mode/__init__.py +1 -0
- tapps_agents/resources/claude/skills/tester/SKILL.md +219 -0
- tapps_agents/resources/claude/skills/tester/__init__.py +1 -0
- tapps_agents/resources/cursor/.cursorignore +35 -0
- tapps_agents/resources/cursor/__init__.py +1 -0
- tapps_agents/resources/cursor/commands/__init__.py +1 -0
- tapps_agents/resources/cursor/commands/build-fix.md +11 -0
- tapps_agents/resources/cursor/commands/build.md +11 -0
- tapps_agents/resources/cursor/commands/e2e.md +11 -0
- tapps_agents/resources/cursor/commands/fix.md +11 -0
- tapps_agents/resources/cursor/commands/refactor-clean.md +11 -0
- tapps_agents/resources/cursor/commands/review.md +11 -0
- tapps_agents/resources/cursor/commands/security-review.md +11 -0
- tapps_agents/resources/cursor/commands/tdd.md +11 -0
- tapps_agents/resources/cursor/commands/test-coverage.md +11 -0
- tapps_agents/resources/cursor/commands/test.md +11 -0
- tapps_agents/resources/cursor/commands/update-codemaps.md +10 -0
- tapps_agents/resources/cursor/commands/update-docs.md +11 -0
- tapps_agents/resources/cursor/rules/__init__.py +1 -0
- tapps_agents/resources/cursor/rules/agent-capabilities.mdc +687 -0
- tapps_agents/resources/cursor/rules/coding-style.mdc +31 -0
- tapps_agents/resources/cursor/rules/command-reference.mdc +2081 -0
- tapps_agents/resources/cursor/rules/cursor-mode-usage.mdc +125 -0
- tapps_agents/resources/cursor/rules/git-workflow.mdc +29 -0
- tapps_agents/resources/cursor/rules/performance.mdc +29 -0
- tapps_agents/resources/cursor/rules/project-context.mdc +163 -0
- tapps_agents/resources/cursor/rules/project-profiling.mdc +197 -0
- tapps_agents/resources/cursor/rules/quick-reference.mdc +630 -0
- tapps_agents/resources/cursor/rules/security.mdc +32 -0
- tapps_agents/resources/cursor/rules/simple-mode.mdc +500 -0
- tapps_agents/resources/cursor/rules/testing.mdc +31 -0
- tapps_agents/resources/cursor/rules/when-to-use.mdc +156 -0
- tapps_agents/resources/cursor/rules/workflow-presets.mdc +179 -0
- tapps_agents/resources/customizations/__init__.py +1 -0
- tapps_agents/resources/customizations/example-custom.yaml +83 -0
- tapps_agents/resources/hooks/__init__.py +1 -0
- tapps_agents/resources/hooks/templates/README.md +5 -0
- tapps_agents/resources/hooks/templates/__init__.py +1 -0
- tapps_agents/resources/hooks/templates/add-project-context.yaml +8 -0
- tapps_agents/resources/hooks/templates/auto-format-js.yaml +10 -0
- tapps_agents/resources/hooks/templates/auto-format-python.yaml +10 -0
- tapps_agents/resources/hooks/templates/git-commit-check.yaml +7 -0
- tapps_agents/resources/hooks/templates/notify-on-complete.yaml +8 -0
- tapps_agents/resources/hooks/templates/quality-gate.yaml +8 -0
- tapps_agents/resources/hooks/templates/security-scan-on-edit.yaml +10 -0
- tapps_agents/resources/hooks/templates/session-end-log.yaml +7 -0
- tapps_agents/resources/hooks/templates/show-beads-ready.yaml +8 -0
- tapps_agents/resources/hooks/templates/test-on-edit.yaml +10 -0
- tapps_agents/resources/hooks/templates/update-docs-on-complete.yaml +8 -0
- tapps_agents/resources/hooks/templates/user-prompt-log.yaml +7 -0
- tapps_agents/resources/scripts/__init__.py +1 -0
- tapps_agents/resources/scripts/set_bd_path.ps1 +51 -0
- tapps_agents/resources/workflows/__init__.py +1 -0
- tapps_agents/resources/workflows/presets/__init__.py +1 -0
- tapps_agents/resources/workflows/presets/brownfield-analysis.yaml +235 -0
- tapps_agents/resources/workflows/presets/fix.yaml +78 -0
- tapps_agents/resources/workflows/presets/full-sdlc.yaml +122 -0
- tapps_agents/resources/workflows/presets/quality.yaml +82 -0
- tapps_agents/resources/workflows/presets/rapid-dev.yaml +84 -0
- tapps_agents/session/__init__.py +19 -19
- tapps_agents/session/manager.py +256 -256
- tapps_agents/simple_mode/__init__.py +66 -66
- tapps_agents/simple_mode/agent_contracts.py +357 -357
- tapps_agents/simple_mode/beads_hooks.py +151 -151
- tapps_agents/simple_mode/code_snippet_handler.py +382 -382
- tapps_agents/simple_mode/documentation_manager.py +395 -395
- tapps_agents/simple_mode/documentation_reader.py +187 -187
- tapps_agents/simple_mode/file_inference.py +292 -292
- tapps_agents/simple_mode/framework_change_detector.py +268 -268
- tapps_agents/simple_mode/intent_parser.py +510 -510
- tapps_agents/simple_mode/learning_progression.py +358 -358
- tapps_agents/simple_mode/nl_handler.py +700 -700
- tapps_agents/simple_mode/onboarding.py +253 -253
- tapps_agents/simple_mode/orchestrators/__init__.py +38 -38
- tapps_agents/simple_mode/orchestrators/breakdown_orchestrator.py +49 -49
- tapps_agents/simple_mode/orchestrators/brownfield_orchestrator.py +135 -135
- tapps_agents/simple_mode/orchestrators/deliverable_checklist.py +349 -349
- tapps_agents/simple_mode/orchestrators/enhance_orchestrator.py +53 -53
- tapps_agents/simple_mode/orchestrators/epic_orchestrator.py +122 -122
- tapps_agents/simple_mode/orchestrators/explore_orchestrator.py +184 -184
- tapps_agents/simple_mode/orchestrators/plan_analysis_orchestrator.py +206 -206
- tapps_agents/simple_mode/orchestrators/pr_orchestrator.py +237 -237
- tapps_agents/simple_mode/orchestrators/refactor_orchestrator.py +222 -222
- tapps_agents/simple_mode/orchestrators/requirements_tracer.py +262 -262
- tapps_agents/simple_mode/orchestrators/resume_orchestrator.py +210 -210
- tapps_agents/simple_mode/orchestrators/review_orchestrator.py +161 -161
- tapps_agents/simple_mode/orchestrators/test_orchestrator.py +82 -82
- tapps_agents/simple_mode/output_aggregator.py +340 -340
- tapps_agents/simple_mode/result_formatters.py +598 -598
- tapps_agents/simple_mode/step_dependencies.py +382 -382
- tapps_agents/simple_mode/step_results.py +276 -276
- tapps_agents/simple_mode/streaming.py +388 -388
- tapps_agents/simple_mode/variations.py +129 -129
- tapps_agents/simple_mode/visual_feedback.py +238 -238
- tapps_agents/simple_mode/zero_config.py +274 -274
- tapps_agents/suggestions/__init__.py +8 -8
- tapps_agents/suggestions/inline_suggester.py +52 -52
- tapps_agents/templates/__init__.py +8 -8
- tapps_agents/templates/microservice_generator.py +274 -274
- tapps_agents/utils/env_validator.py +291 -291
- tapps_agents/workflow/__init__.py +171 -171
- tapps_agents/workflow/acceptance_verifier.py +132 -132
- tapps_agents/workflow/agent_handlers/__init__.py +41 -41
- tapps_agents/workflow/agent_handlers/analyst_handler.py +75 -75
- tapps_agents/workflow/agent_handlers/architect_handler.py +107 -107
- tapps_agents/workflow/agent_handlers/base.py +84 -84
- tapps_agents/workflow/agent_handlers/debugger_handler.py +100 -100
- tapps_agents/workflow/agent_handlers/designer_handler.py +110 -110
- tapps_agents/workflow/agent_handlers/documenter_handler.py +94 -94
- tapps_agents/workflow/agent_handlers/implementer_handler.py +235 -235
- tapps_agents/workflow/agent_handlers/ops_handler.py +62 -62
- tapps_agents/workflow/agent_handlers/orchestrator_handler.py +43 -43
- tapps_agents/workflow/agent_handlers/planner_handler.py +98 -98
- tapps_agents/workflow/agent_handlers/registry.py +119 -119
- tapps_agents/workflow/agent_handlers/reviewer_handler.py +119 -119
- tapps_agents/workflow/agent_handlers/tester_handler.py +69 -69
- tapps_agents/workflow/analytics_accessor.py +337 -337
- tapps_agents/workflow/analytics_alerts.py +416 -416
- tapps_agents/workflow/analytics_dashboard_cursor.py +281 -281
- tapps_agents/workflow/analytics_dual_write.py +103 -103
- tapps_agents/workflow/analytics_integration.py +119 -119
- tapps_agents/workflow/analytics_query_parser.py +278 -278
- tapps_agents/workflow/analytics_visualizer.py +259 -259
- tapps_agents/workflow/artifact_helper.py +204 -204
- tapps_agents/workflow/audit_logger.py +263 -263
- tapps_agents/workflow/auto_execution_config.py +340 -340
- tapps_agents/workflow/auto_progression.py +586 -586
- tapps_agents/workflow/branch_cleanup.py +349 -349
- tapps_agents/workflow/checkpoint.py +256 -256
- tapps_agents/workflow/checkpoint_manager.py +178 -178
- tapps_agents/workflow/code_artifact.py +179 -179
- tapps_agents/workflow/common_enums.py +96 -96
- tapps_agents/workflow/confirmation_handler.py +130 -130
- tapps_agents/workflow/context_analyzer.py +222 -222
- tapps_agents/workflow/context_artifact.py +230 -230
- tapps_agents/workflow/cursor_chat.py +94 -94
- tapps_agents/workflow/cursor_skill_helper.py +516 -516
- tapps_agents/workflow/dependency_resolver.py +244 -244
- tapps_agents/workflow/design_artifact.py +156 -156
- tapps_agents/workflow/detector.py +751 -751
- tapps_agents/workflow/direct_execution_fallback.py +301 -301
- tapps_agents/workflow/docs_artifact.py +168 -168
- tapps_agents/workflow/enforcer.py +389 -389
- tapps_agents/workflow/enhancement_artifact.py +142 -142
- tapps_agents/workflow/error_recovery.py +806 -806
- tapps_agents/workflow/event_bus.py +183 -183
- tapps_agents/workflow/event_log.py +612 -612
- tapps_agents/workflow/events.py +63 -63
- tapps_agents/workflow/exceptions.py +43 -43
- tapps_agents/workflow/execution_graph.py +498 -498
- tapps_agents/workflow/execution_plan.py +126 -126
- tapps_agents/workflow/file_utils.py +186 -186
- tapps_agents/workflow/gate_evaluator.py +182 -182
- tapps_agents/workflow/gate_integration.py +200 -200
- tapps_agents/workflow/graph_visualizer.py +130 -130
- tapps_agents/workflow/health_checker.py +206 -206
- tapps_agents/workflow/logging_helper.py +243 -243
- tapps_agents/workflow/manifest.py +582 -582
- tapps_agents/workflow/marker_writer.py +250 -250
- tapps_agents/workflow/messaging.py +325 -325
- tapps_agents/workflow/metadata_models.py +91 -91
- tapps_agents/workflow/metrics_integration.py +226 -226
- tapps_agents/workflow/migration_utils.py +116 -116
- tapps_agents/workflow/models.py +148 -148
- tapps_agents/workflow/nlp_config.py +198 -198
- tapps_agents/workflow/nlp_error_handler.py +207 -207
- tapps_agents/workflow/nlp_executor.py +163 -163
- tapps_agents/workflow/nlp_parser.py +528 -528
- tapps_agents/workflow/observability_dashboard.py +451 -451
- tapps_agents/workflow/observer.py +170 -170
- tapps_agents/workflow/ops_artifact.py +257 -257
- tapps_agents/workflow/output_passing.py +214 -214
- tapps_agents/workflow/parallel_executor.py +463 -463
- tapps_agents/workflow/planning_artifact.py +179 -179
- tapps_agents/workflow/preset_loader.py +285 -285
- tapps_agents/workflow/preset_recommender.py +270 -270
- tapps_agents/workflow/progress_logger.py +145 -145
- tapps_agents/workflow/progress_manager.py +303 -303
- tapps_agents/workflow/progress_monitor.py +186 -186
- tapps_agents/workflow/progress_updates.py +423 -423
- tapps_agents/workflow/quality_artifact.py +158 -158
- tapps_agents/workflow/quality_loopback.py +101 -101
- tapps_agents/workflow/recommender.py +387 -387
- tapps_agents/workflow/remediation_loop.py +166 -166
- tapps_agents/workflow/result_aggregator.py +300 -300
- tapps_agents/workflow/review_artifact.py +185 -185
- tapps_agents/workflow/schema_validator.py +522 -522
- tapps_agents/workflow/session_handoff.py +178 -178
- tapps_agents/workflow/skill_invoker.py +648 -648
- tapps_agents/workflow/state_manager.py +756 -756
- tapps_agents/workflow/state_persistence_config.py +331 -331
- tapps_agents/workflow/status_monitor.py +449 -449
- tapps_agents/workflow/step_checkpoint.py +314 -314
- tapps_agents/workflow/step_details.py +201 -201
- tapps_agents/workflow/story_models.py +147 -147
- tapps_agents/workflow/streaming.py +416 -416
- tapps_agents/workflow/suggestion_engine.py +552 -552
- tapps_agents/workflow/testing_artifact.py +186 -186
- tapps_agents/workflow/timeline.py +158 -158
- tapps_agents/workflow/token_integration.py +209 -209
- tapps_agents/workflow/validation.py +217 -217
- tapps_agents/workflow/visual_feedback.py +391 -391
- tapps_agents/workflow/workflow_chain.py +95 -95
- tapps_agents/workflow/workflow_summary.py +219 -219
- tapps_agents/workflow/worktree_manager.py +724 -724
- {tapps_agents-3.6.0.dist-info → tapps_agents-3.6.1.dist-info}/METADATA +672 -672
- tapps_agents-3.6.1.dist-info/RECORD +883 -0
- {tapps_agents-3.6.0.dist-info → tapps_agents-3.6.1.dist-info}/licenses/LICENSE +22 -22
- tapps_agents-3.6.0.dist-info/RECORD +0 -758
- {tapps_agents-3.6.0.dist-info → tapps_agents-3.6.1.dist-info}/WHEEL +0 -0
- {tapps_agents-3.6.0.dist-info → tapps_agents-3.6.1.dist-info}/entry_points.txt +0 -0
- {tapps_agents-3.6.0.dist-info → tapps_agents-3.6.1.dist-info}/top_level.txt +0 -0
|
@@ -1,581 +1,581 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Parallel Tool Executor - Async execution of quality tools with timeout protection.
|
|
3
|
-
|
|
4
|
-
This module provides parallel execution capabilities for ReviewerAgent quality tools,
|
|
5
|
-
achieving 2-3x performance improvement through concurrent tool execution.
|
|
6
|
-
|
|
7
|
-
Architecture:
|
|
8
|
-
- Phase 1: Parallel batch (Ruff, mypy, bandit, pip-audit)
|
|
9
|
-
- Phase 2: Sequential batch (jscpd - requires full project context)
|
|
10
|
-
- Error recovery via asyncio.gather(return_exceptions=True)
|
|
11
|
-
- Timeout protection via asyncio.wait_for()
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
import asyncio
|
|
15
|
-
import logging
|
|
16
|
-
import time
|
|
17
|
-
from dataclasses import dataclass, field
|
|
18
|
-
from enum import Enum
|
|
19
|
-
from pathlib import Path
|
|
20
|
-
from typing import Any, Callable, Dict, List, Optional, Union
|
|
21
|
-
|
|
22
|
-
logger = logging.getLogger(__name__)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
# ============================================================================
|
|
26
|
-
# Data Models
|
|
27
|
-
# ============================================================================
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
@dataclass(frozen=True)
|
|
31
|
-
class ToolExecutionConfig:
|
|
32
|
-
"""
|
|
33
|
-
Configuration for parallel tool execution.
|
|
34
|
-
|
|
35
|
-
Attributes:
|
|
36
|
-
enabled: Master switch for parallel execution
|
|
37
|
-
timeout_seconds: Global timeout for all tools (seconds)
|
|
38
|
-
max_concurrent_tools: Maximum concurrent subprocesses
|
|
39
|
-
fallback_to_sequential: Auto-fallback on async errors
|
|
40
|
-
tool_timeouts: Per-tool timeout overrides (optional)
|
|
41
|
-
|
|
42
|
-
Example:
|
|
43
|
-
>>> config = ToolExecutionConfig(
|
|
44
|
-
... enabled=True,
|
|
45
|
-
... timeout_seconds=30,
|
|
46
|
-
... max_concurrent_tools=4
|
|
47
|
-
... )
|
|
48
|
-
"""
|
|
49
|
-
enabled: bool = True
|
|
50
|
-
timeout_seconds: int = 30
|
|
51
|
-
max_concurrent_tools: int = 4
|
|
52
|
-
fallback_to_sequential: bool = True
|
|
53
|
-
tool_timeouts: Optional[Dict[str, int]] = None
|
|
54
|
-
|
|
55
|
-
def __post_init__(self):
|
|
56
|
-
"""Validate configuration values."""
|
|
57
|
-
# Use object.__setattr__ for frozen dataclass validation
|
|
58
|
-
if self.timeout_seconds <= 0:
|
|
59
|
-
raise ValueError("timeout_seconds must be positive")
|
|
60
|
-
if self.max_concurrent_tools <= 0:
|
|
61
|
-
raise ValueError("max_concurrent_tools must be positive")
|
|
62
|
-
if self.tool_timeouts is not None:
|
|
63
|
-
for tool, timeout in self.tool_timeouts.items():
|
|
64
|
-
if timeout <= 0:
|
|
65
|
-
raise ValueError(
|
|
66
|
-
f"tool_timeouts[{tool}] must be positive, got {timeout}"
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
def get_tool_timeout(self, tool_name: str) -> int:
|
|
70
|
-
"""
|
|
71
|
-
Get timeout for specific tool (with fallback to global).
|
|
72
|
-
|
|
73
|
-
Args:
|
|
74
|
-
tool_name: Name of the tool (ruff, mypy, etc.)
|
|
75
|
-
|
|
76
|
-
Returns:
|
|
77
|
-
Timeout in seconds for the tool
|
|
78
|
-
|
|
79
|
-
Example:
|
|
80
|
-
>>> config.get_tool_timeout("mypy")
|
|
81
|
-
45 # per-tool override
|
|
82
|
-
>>> config.get_tool_timeout("ruff")
|
|
83
|
-
30 # global fallback
|
|
84
|
-
"""
|
|
85
|
-
if self.tool_timeouts and tool_name in self.tool_timeouts:
|
|
86
|
-
return self.tool_timeouts[tool_name]
|
|
87
|
-
return self.timeout_seconds
|
|
88
|
-
|
|
89
|
-
@classmethod
|
|
90
|
-
def from_dict(cls, config_dict: Dict[str, Any]) -> "ToolExecutionConfig":
|
|
91
|
-
"""
|
|
92
|
-
Create config from dictionary (loaded from YAML).
|
|
93
|
-
|
|
94
|
-
Args:
|
|
95
|
-
config_dict: Configuration dictionary
|
|
96
|
-
|
|
97
|
-
Returns:
|
|
98
|
-
ToolExecutionConfig instance
|
|
99
|
-
|
|
100
|
-
Example:
|
|
101
|
-
>>> config = ToolExecutionConfig.from_dict({
|
|
102
|
-
... "enabled": True,
|
|
103
|
-
... "timeout_seconds": 30
|
|
104
|
-
... })
|
|
105
|
-
"""
|
|
106
|
-
return cls(
|
|
107
|
-
enabled=config_dict.get("enabled", True),
|
|
108
|
-
timeout_seconds=config_dict.get("timeout_seconds", 30),
|
|
109
|
-
max_concurrent_tools=config_dict.get("max_concurrent_tools", 4),
|
|
110
|
-
fallback_to_sequential=config_dict.get("fallback_to_sequential", True),
|
|
111
|
-
tool_timeouts=config_dict.get("tool_timeouts"),
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
class ToolStatus(str, Enum):
|
|
116
|
-
"""Status of tool execution."""
|
|
117
|
-
SUCCESS = "success"
|
|
118
|
-
TIMEOUT = "timeout"
|
|
119
|
-
ERROR = "error"
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
@dataclass(frozen=True)
|
|
123
|
-
class ToolResult:
|
|
124
|
-
"""
|
|
125
|
-
Result from single tool execution.
|
|
126
|
-
|
|
127
|
-
Attributes:
|
|
128
|
-
tool: Tool name (ruff, mypy, bandit, pip-audit, jscpd)
|
|
129
|
-
status: Execution status (success, timeout, error)
|
|
130
|
-
data: Tool-specific result data (None if timeout/error)
|
|
131
|
-
duration: Execution time in seconds
|
|
132
|
-
error: Error message if status != success
|
|
133
|
-
exit_code: Tool exit code (None if timeout)
|
|
134
|
-
|
|
135
|
-
Example (success):
|
|
136
|
-
>>> result = ToolResult(
|
|
137
|
-
... tool="ruff",
|
|
138
|
-
... status=ToolStatus.SUCCESS,
|
|
139
|
-
... data={"issues": []},
|
|
140
|
-
... duration=2.3,
|
|
141
|
-
... error=None
|
|
142
|
-
... )
|
|
143
|
-
|
|
144
|
-
Example (timeout):
|
|
145
|
-
>>> result = ToolResult(
|
|
146
|
-
... tool="mypy",
|
|
147
|
-
... status=ToolStatus.TIMEOUT,
|
|
148
|
-
... data=None,
|
|
149
|
-
... duration=30.5,
|
|
150
|
-
... error="Timeout after 30.5s"
|
|
151
|
-
... )
|
|
152
|
-
|
|
153
|
-
Example (error):
|
|
154
|
-
>>> result = ToolResult(
|
|
155
|
-
... tool="bandit",
|
|
156
|
-
... status=ToolStatus.ERROR,
|
|
157
|
-
... data=None,
|
|
158
|
-
... duration=1.2,
|
|
159
|
-
... error="subprocess returned exit code 2",
|
|
160
|
-
... exit_code=2
|
|
161
|
-
... )
|
|
162
|
-
"""
|
|
163
|
-
tool: str
|
|
164
|
-
status: ToolStatus
|
|
165
|
-
data: Optional[Any]
|
|
166
|
-
duration: float
|
|
167
|
-
error: Optional[str] = None
|
|
168
|
-
exit_code: Optional[int] = None
|
|
169
|
-
|
|
170
|
-
def is_success(self) -> bool:
|
|
171
|
-
"""Check if tool executed successfully."""
|
|
172
|
-
return self.status == ToolStatus.SUCCESS
|
|
173
|
-
|
|
174
|
-
def is_timeout(self) -> bool:
|
|
175
|
-
"""Check if tool timed out."""
|
|
176
|
-
return self.status == ToolStatus.TIMEOUT
|
|
177
|
-
|
|
178
|
-
def is_error(self) -> bool:
|
|
179
|
-
"""Check if tool failed with error."""
|
|
180
|
-
return self.status == ToolStatus.ERROR
|
|
181
|
-
|
|
182
|
-
def to_dict(self) -> Dict[str, Any]:
|
|
183
|
-
"""Convert to dictionary for serialization."""
|
|
184
|
-
return {
|
|
185
|
-
"tool": self.tool,
|
|
186
|
-
"status": self.status.value,
|
|
187
|
-
"data": self.data,
|
|
188
|
-
"duration": self.duration,
|
|
189
|
-
"error": self.error,
|
|
190
|
-
"exit_code": self.exit_code,
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
# ============================================================================
|
|
195
|
-
# Parallel Tool Executor
|
|
196
|
-
# ============================================================================
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
class ParallelToolExecutor:
|
|
200
|
-
"""
|
|
201
|
-
Coordinates parallel execution of quality tools with timeout protection.
|
|
202
|
-
|
|
203
|
-
Architecture:
|
|
204
|
-
- Phase 1: Parallel batch (Ruff, mypy, bandit, pip-audit)
|
|
205
|
-
- Phase 2: Sequential batch (jscpd - requires full context)
|
|
206
|
-
- Error recovery via asyncio.gather(return_exceptions=True)
|
|
207
|
-
- Timeout protection via asyncio.wait_for()
|
|
208
|
-
|
|
209
|
-
Example:
|
|
210
|
-
>>> config = ToolExecutionConfig(enabled=True, timeout_seconds=30)
|
|
211
|
-
>>> executor = ParallelToolExecutor(config)
|
|
212
|
-
>>> results = await executor.execute_parallel(Path("src/example.py"))
|
|
213
|
-
"""
|
|
214
|
-
|
|
215
|
-
def __init__(self, config: ToolExecutionConfig):
|
|
216
|
-
"""
|
|
217
|
-
Initialize parallel tool executor.
|
|
218
|
-
|
|
219
|
-
Args:
|
|
220
|
-
config: Tool execution configuration
|
|
221
|
-
"""
|
|
222
|
-
self.config = config
|
|
223
|
-
self.logger = logging.getLogger(__name__)
|
|
224
|
-
|
|
225
|
-
async def execute_parallel(
|
|
226
|
-
self,
|
|
227
|
-
file_path: Path,
|
|
228
|
-
tool_runners: Optional[Dict[str, Callable]] = None,
|
|
229
|
-
) -> List[ToolResult]:
|
|
230
|
-
"""
|
|
231
|
-
Execute all quality tools with parallel optimization.
|
|
232
|
-
|
|
233
|
-
Args:
|
|
234
|
-
file_path: Path to file to review
|
|
235
|
-
tool_runners: Optional dict of tool functions (for testing/customization)
|
|
236
|
-
|
|
237
|
-
Returns:
|
|
238
|
-
List of ToolResult objects (successful, timeout, or error)
|
|
239
|
-
|
|
240
|
-
Raises:
|
|
241
|
-
Never raises - always returns partial results on failures
|
|
242
|
-
|
|
243
|
-
Example:
|
|
244
|
-
>>> results = await executor.execute_parallel(Path("src/example.py"))
|
|
245
|
-
>>> successful = [r for r in results if r.is_success()]
|
|
246
|
-
>>> len(successful)
|
|
247
|
-
4 # Ruff, mypy, bandit, pip-audit succeeded
|
|
248
|
-
|
|
249
|
-
Performance:
|
|
250
|
-
- Sequential: ~23s (sum of all tools)
|
|
251
|
-
- Parallel: ~12s (2x faster)
|
|
252
|
-
"""
|
|
253
|
-
if not self.config.enabled:
|
|
254
|
-
self.logger.info("Parallel execution disabled, using sequential fallback")
|
|
255
|
-
return await self._execute_sequential(file_path, tool_runners)
|
|
256
|
-
|
|
257
|
-
try:
|
|
258
|
-
# Phase 1: Parallel batch
|
|
259
|
-
self.logger.debug("Starting parallel batch execution")
|
|
260
|
-
parallel_results = await self._execute_parallel_batch(
|
|
261
|
-
file_path, tool_runners
|
|
262
|
-
)
|
|
263
|
-
|
|
264
|
-
# Phase 2: Sequential batch (jscpd requires full context)
|
|
265
|
-
self.logger.debug("Starting sequential batch execution")
|
|
266
|
-
sequential_results = await self._execute_sequential_batch(
|
|
267
|
-
file_path, tool_runners
|
|
268
|
-
)
|
|
269
|
-
|
|
270
|
-
all_results = parallel_results + sequential_results
|
|
271
|
-
|
|
272
|
-
# Log summary
|
|
273
|
-
successful = [r for r in all_results if r.is_success()]
|
|
274
|
-
failed = [r for r in all_results if not r.is_success()]
|
|
275
|
-
total_time = sum(r.duration for r in all_results)
|
|
276
|
-
|
|
277
|
-
self.logger.info(
|
|
278
|
-
f"Parallel execution complete: {len(successful)}/{len(all_results)} "
|
|
279
|
-
f"tools successful, total time: {total_time:.2f}s"
|
|
280
|
-
)
|
|
281
|
-
|
|
282
|
-
if failed:
|
|
283
|
-
self.logger.warning(
|
|
284
|
-
f"{len(failed)} tools failed: "
|
|
285
|
-
f"{', '.join(r.tool for r in failed)}"
|
|
286
|
-
)
|
|
287
|
-
|
|
288
|
-
return all_results
|
|
289
|
-
|
|
290
|
-
except Exception as e:
|
|
291
|
-
self.logger.error(f"Parallel execution failed: {e}", exc_info=True)
|
|
292
|
-
|
|
293
|
-
if self.config.fallback_to_sequential:
|
|
294
|
-
self.logger.warning("Falling back to sequential execution")
|
|
295
|
-
return await self._execute_sequential(file_path, tool_runners)
|
|
296
|
-
|
|
297
|
-
# Return empty list on catastrophic failure (defensive programming)
|
|
298
|
-
self.logger.error("No fallback configured, returning empty results")
|
|
299
|
-
return []
|
|
300
|
-
|
|
301
|
-
async def _execute_parallel_batch(
|
|
302
|
-
self,
|
|
303
|
-
file_path: Path,
|
|
304
|
-
tool_runners: Optional[Dict[str, Callable]] = None,
|
|
305
|
-
) -> List[ToolResult]:
|
|
306
|
-
"""
|
|
307
|
-
Execute independent tools concurrently.
|
|
308
|
-
|
|
309
|
-
Args:
|
|
310
|
-
file_path: Path to file to analyze
|
|
311
|
-
tool_runners: Optional dict of tool functions
|
|
312
|
-
|
|
313
|
-
Returns:
|
|
314
|
-
List of ToolResult objects from parallel execution
|
|
315
|
-
"""
|
|
316
|
-
# Default tool runners (can be overridden for testing)
|
|
317
|
-
if tool_runners is None:
|
|
318
|
-
tool_runners = {}
|
|
319
|
-
|
|
320
|
-
# Build list of tasks for parallel execution
|
|
321
|
-
tasks = []
|
|
322
|
-
parallel_tools = ["ruff", "mypy", "bandit", "pip_audit"]
|
|
323
|
-
|
|
324
|
-
for tool_name in parallel_tools:
|
|
325
|
-
if tool_name in tool_runners:
|
|
326
|
-
tool_func = tool_runners[tool_name]
|
|
327
|
-
tasks.append(
|
|
328
|
-
self.execute_with_timeout(tool_func, file_path, tool_name)
|
|
329
|
-
)
|
|
330
|
-
|
|
331
|
-
if not tasks:
|
|
332
|
-
self.logger.warning("No parallel tools configured")
|
|
333
|
-
return []
|
|
334
|
-
|
|
335
|
-
# Execute all tasks concurrently
|
|
336
|
-
# return_exceptions=True: Continue on individual failures
|
|
337
|
-
results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
338
|
-
|
|
339
|
-
# Process results (convert exceptions to ToolResult objects)
|
|
340
|
-
return self._process_results(results)
|
|
341
|
-
|
|
342
|
-
async def _execute_sequential_batch(
|
|
343
|
-
self,
|
|
344
|
-
file_path: Path,
|
|
345
|
-
tool_runners: Optional[Dict[str, Callable]] = None,
|
|
346
|
-
) -> List[ToolResult]:
|
|
347
|
-
"""
|
|
348
|
-
Execute context-dependent tools sequentially.
|
|
349
|
-
|
|
350
|
-
Args:
|
|
351
|
-
file_path: Path to file to analyze
|
|
352
|
-
tool_runners: Optional dict of tool functions
|
|
353
|
-
|
|
354
|
-
Returns:
|
|
355
|
-
List of ToolResult objects from sequential execution
|
|
356
|
-
"""
|
|
357
|
-
if tool_runners is None:
|
|
358
|
-
tool_runners = {}
|
|
359
|
-
|
|
360
|
-
# jscpd requires full project context (cannot parallelize effectively)
|
|
361
|
-
if "jscpd" not in tool_runners:
|
|
362
|
-
self.logger.debug("No sequential tools configured")
|
|
363
|
-
return []
|
|
364
|
-
|
|
365
|
-
jscpd_result = await self.execute_with_timeout(
|
|
366
|
-
tool_runners["jscpd"], file_path, "jscpd"
|
|
367
|
-
)
|
|
368
|
-
|
|
369
|
-
return [self._process_single_result(jscpd_result)]
|
|
370
|
-
|
|
371
|
-
async def _execute_sequential(
|
|
372
|
-
self,
|
|
373
|
-
file_path: Path,
|
|
374
|
-
tool_runners: Optional[Dict[str, Callable]] = None,
|
|
375
|
-
) -> List[ToolResult]:
|
|
376
|
-
"""
|
|
377
|
-
Fallback: Execute all tools sequentially.
|
|
378
|
-
|
|
379
|
-
Args:
|
|
380
|
-
file_path: Path to file to analyze
|
|
381
|
-
tool_runners: Optional dict of tool functions
|
|
382
|
-
|
|
383
|
-
Returns:
|
|
384
|
-
List of ToolResult objects from sequential execution
|
|
385
|
-
"""
|
|
386
|
-
if tool_runners is None:
|
|
387
|
-
tool_runners = {}
|
|
388
|
-
|
|
389
|
-
results = []
|
|
390
|
-
all_tools = ["ruff", "mypy", "bandit", "pip_audit", "jscpd"]
|
|
391
|
-
|
|
392
|
-
for tool_name in all_tools:
|
|
393
|
-
if tool_name in tool_runners:
|
|
394
|
-
result = await self.execute_with_timeout(
|
|
395
|
-
tool_runners[tool_name], file_path, tool_name
|
|
396
|
-
)
|
|
397
|
-
results.append(result)
|
|
398
|
-
|
|
399
|
-
return results
|
|
400
|
-
|
|
401
|
-
async def execute_with_timeout(
|
|
402
|
-
self,
|
|
403
|
-
tool_func: Callable[[Path], Any],
|
|
404
|
-
file_path: Path,
|
|
405
|
-
tool_name: Optional[str] = None,
|
|
406
|
-
) -> ToolResult:
|
|
407
|
-
"""
|
|
408
|
-
Execute single tool with timeout protection.
|
|
409
|
-
|
|
410
|
-
Args:
|
|
411
|
-
tool_func: Async tool function to execute
|
|
412
|
-
file_path: Path to file to analyze
|
|
413
|
-
tool_name: Tool name for logging (auto-detected if None)
|
|
414
|
-
|
|
415
|
-
Returns:
|
|
416
|
-
ToolResult (success, timeout, or error)
|
|
417
|
-
|
|
418
|
-
Example:
|
|
419
|
-
>>> result = await executor.execute_with_timeout(
|
|
420
|
-
... run_ruff_async,
|
|
421
|
-
... Path("src/example.py")
|
|
422
|
-
... )
|
|
423
|
-
>>> result.status
|
|
424
|
-
ToolStatus.SUCCESS
|
|
425
|
-
>>> result.duration
|
|
426
|
-
2.3
|
|
427
|
-
|
|
428
|
-
Timeout Handling:
|
|
429
|
-
- Global timeout: config.timeout_seconds
|
|
430
|
-
- Per-tool timeout: config.get_tool_timeout(tool_name)
|
|
431
|
-
- Timeout exception → ToolResult with status=TIMEOUT
|
|
432
|
-
"""
|
|
433
|
-
# Extract tool name from function name if not provided
|
|
434
|
-
if tool_name is None:
|
|
435
|
-
tool_name = tool_func.__name__.replace("run_", "").replace("_async", "")
|
|
436
|
-
|
|
437
|
-
# Get timeout for this tool
|
|
438
|
-
timeout = self.config.get_tool_timeout(tool_name)
|
|
439
|
-
|
|
440
|
-
start_time = time.time()
|
|
441
|
-
|
|
442
|
-
try:
|
|
443
|
-
# Execute with timeout protection
|
|
444
|
-
result = await asyncio.wait_for(
|
|
445
|
-
tool_func(file_path), timeout=timeout
|
|
446
|
-
)
|
|
447
|
-
|
|
448
|
-
duration = time.time() - start_time
|
|
449
|
-
self.logger.debug(f"{tool_name} completed in {duration:.2f}s")
|
|
450
|
-
|
|
451
|
-
return ToolResult(
|
|
452
|
-
tool=tool_name,
|
|
453
|
-
status=ToolStatus.SUCCESS,
|
|
454
|
-
data=result,
|
|
455
|
-
duration=duration,
|
|
456
|
-
error=None,
|
|
457
|
-
exit_code=None,
|
|
458
|
-
)
|
|
459
|
-
|
|
460
|
-
except asyncio.TimeoutError:
|
|
461
|
-
duration = time.time() - start_time
|
|
462
|
-
error_msg = f"Timeout after {duration:.2f}s (limit: {timeout}s)"
|
|
463
|
-
|
|
464
|
-
self.logger.warning(f"{tool_name}: {error_msg}")
|
|
465
|
-
|
|
466
|
-
return self._handle_timeout(tool_name, duration, timeout)
|
|
467
|
-
|
|
468
|
-
except Exception as e:
|
|
469
|
-
duration = time.time() - start_time
|
|
470
|
-
self.logger.error(
|
|
471
|
-
f"{tool_name} failed after {duration:.2f}s: {e}",
|
|
472
|
-
exc_info=True,
|
|
473
|
-
)
|
|
474
|
-
|
|
475
|
-
return self._handle_error(tool_name, e, duration)
|
|
476
|
-
|
|
477
|
-
def _handle_timeout(
|
|
478
|
-
self, tool_name: str, duration: float, timeout: int
|
|
479
|
-
) -> ToolResult:
|
|
480
|
-
"""
|
|
481
|
-
Create timeout result annotation.
|
|
482
|
-
|
|
483
|
-
Args:
|
|
484
|
-
tool_name: Name of the tool that timed out
|
|
485
|
-
duration: Actual execution time
|
|
486
|
-
timeout: Configured timeout limit
|
|
487
|
-
|
|
488
|
-
Returns:
|
|
489
|
-
ToolResult with status=TIMEOUT
|
|
490
|
-
"""
|
|
491
|
-
return ToolResult(
|
|
492
|
-
tool=tool_name,
|
|
493
|
-
status=ToolStatus.TIMEOUT,
|
|
494
|
-
data=None,
|
|
495
|
-
duration=duration,
|
|
496
|
-
error=f"Timeout after {duration:.2f}s (limit: {timeout}s)",
|
|
497
|
-
exit_code=None,
|
|
498
|
-
)
|
|
499
|
-
|
|
500
|
-
def _handle_error(
|
|
501
|
-
self, tool_name: str, exception: Exception, duration: float
|
|
502
|
-
) -> ToolResult:
|
|
503
|
-
"""
|
|
504
|
-
Create error result annotation.
|
|
505
|
-
|
|
506
|
-
Args:
|
|
507
|
-
tool_name: Name of the tool that failed
|
|
508
|
-
exception: Exception that was raised
|
|
509
|
-
duration: Execution time before failure
|
|
510
|
-
|
|
511
|
-
Returns:
|
|
512
|
-
ToolResult with status=ERROR
|
|
513
|
-
"""
|
|
514
|
-
# Extract exit code from subprocess errors
|
|
515
|
-
exit_code = None
|
|
516
|
-
if hasattr(exception, "returncode"):
|
|
517
|
-
exit_code = exception.returncode
|
|
518
|
-
|
|
519
|
-
return ToolResult(
|
|
520
|
-
tool=tool_name,
|
|
521
|
-
status=ToolStatus.ERROR,
|
|
522
|
-
data=None,
|
|
523
|
-
duration=duration,
|
|
524
|
-
error=str(exception),
|
|
525
|
-
exit_code=exit_code,
|
|
526
|
-
)
|
|
527
|
-
|
|
528
|
-
def _process_results(
|
|
529
|
-
self, results: List[Union[ToolResult, Exception]]
|
|
530
|
-
) -> List[ToolResult]:
|
|
531
|
-
"""
|
|
532
|
-
Convert exceptions to error ToolResults.
|
|
533
|
-
|
|
534
|
-
Args:
|
|
535
|
-
results: List of ToolResult or Exception objects
|
|
536
|
-
|
|
537
|
-
Returns:
|
|
538
|
-
List of ToolResult objects (exceptions converted to errors)
|
|
539
|
-
"""
|
|
540
|
-
processed = []
|
|
541
|
-
|
|
542
|
-
for result in results:
|
|
543
|
-
if isinstance(result, Exception):
|
|
544
|
-
# Exception from asyncio.gather (shouldn't happen with our error handling)
|
|
545
|
-
self.logger.error(f"Unexpected exception in gather: {result}")
|
|
546
|
-
processed.append(
|
|
547
|
-
self._handle_error("unknown", result, 0.0)
|
|
548
|
-
)
|
|
549
|
-
elif isinstance(result, ToolResult):
|
|
550
|
-
processed.append(result)
|
|
551
|
-
else:
|
|
552
|
-
# Unexpected result type (defensive programming)
|
|
553
|
-
self.logger.error(f"Unexpected result type: {type(result)}")
|
|
554
|
-
processed.append(
|
|
555
|
-
ToolResult(
|
|
556
|
-
tool="unknown",
|
|
557
|
-
status=ToolStatus.ERROR,
|
|
558
|
-
data=None,
|
|
559
|
-
duration=0.0,
|
|
560
|
-
error=f"Unexpected result type: {type(result)}",
|
|
561
|
-
exit_code=None,
|
|
562
|
-
)
|
|
563
|
-
)
|
|
564
|
-
|
|
565
|
-
return processed
|
|
566
|
-
|
|
567
|
-
def _process_single_result(
|
|
568
|
-
self, result: Union[ToolResult, Exception]
|
|
569
|
-
) -> ToolResult:
|
|
570
|
-
"""
|
|
571
|
-
Process single result (for sequential execution).
|
|
572
|
-
|
|
573
|
-
Args:
|
|
574
|
-
result: ToolResult or Exception
|
|
575
|
-
|
|
576
|
-
Returns:
|
|
577
|
-
ToolResult object
|
|
578
|
-
"""
|
|
579
|
-
if isinstance(result, Exception):
|
|
580
|
-
return self._handle_error("unknown", result, 0.0)
|
|
581
|
-
return result
|
|
1
|
+
"""
|
|
2
|
+
Parallel Tool Executor - Async execution of quality tools with timeout protection.
|
|
3
|
+
|
|
4
|
+
This module provides parallel execution capabilities for ReviewerAgent quality tools,
|
|
5
|
+
achieving 2-3x performance improvement through concurrent tool execution.
|
|
6
|
+
|
|
7
|
+
Architecture:
|
|
8
|
+
- Phase 1: Parallel batch (Ruff, mypy, bandit, pip-audit)
|
|
9
|
+
- Phase 2: Sequential batch (jscpd - requires full project context)
|
|
10
|
+
- Error recovery via asyncio.gather(return_exceptions=True)
|
|
11
|
+
- Timeout protection via asyncio.wait_for()
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import asyncio
|
|
15
|
+
import logging
|
|
16
|
+
import time
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from enum import Enum
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Any, Callable, Dict, List, Optional, Union
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# ============================================================================
|
|
26
|
+
# Data Models
|
|
27
|
+
# ============================================================================
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True)
|
|
31
|
+
class ToolExecutionConfig:
|
|
32
|
+
"""
|
|
33
|
+
Configuration for parallel tool execution.
|
|
34
|
+
|
|
35
|
+
Attributes:
|
|
36
|
+
enabled: Master switch for parallel execution
|
|
37
|
+
timeout_seconds: Global timeout for all tools (seconds)
|
|
38
|
+
max_concurrent_tools: Maximum concurrent subprocesses
|
|
39
|
+
fallback_to_sequential: Auto-fallback on async errors
|
|
40
|
+
tool_timeouts: Per-tool timeout overrides (optional)
|
|
41
|
+
|
|
42
|
+
Example:
|
|
43
|
+
>>> config = ToolExecutionConfig(
|
|
44
|
+
... enabled=True,
|
|
45
|
+
... timeout_seconds=30,
|
|
46
|
+
... max_concurrent_tools=4
|
|
47
|
+
... )
|
|
48
|
+
"""
|
|
49
|
+
enabled: bool = True
|
|
50
|
+
timeout_seconds: int = 30
|
|
51
|
+
max_concurrent_tools: int = 4
|
|
52
|
+
fallback_to_sequential: bool = True
|
|
53
|
+
tool_timeouts: Optional[Dict[str, int]] = None
|
|
54
|
+
|
|
55
|
+
def __post_init__(self):
|
|
56
|
+
"""Validate configuration values."""
|
|
57
|
+
# Use object.__setattr__ for frozen dataclass validation
|
|
58
|
+
if self.timeout_seconds <= 0:
|
|
59
|
+
raise ValueError("timeout_seconds must be positive")
|
|
60
|
+
if self.max_concurrent_tools <= 0:
|
|
61
|
+
raise ValueError("max_concurrent_tools must be positive")
|
|
62
|
+
if self.tool_timeouts is not None:
|
|
63
|
+
for tool, timeout in self.tool_timeouts.items():
|
|
64
|
+
if timeout <= 0:
|
|
65
|
+
raise ValueError(
|
|
66
|
+
f"tool_timeouts[{tool}] must be positive, got {timeout}"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def get_tool_timeout(self, tool_name: str) -> int:
|
|
70
|
+
"""
|
|
71
|
+
Get timeout for specific tool (with fallback to global).
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
tool_name: Name of the tool (ruff, mypy, etc.)
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Timeout in seconds for the tool
|
|
78
|
+
|
|
79
|
+
Example:
|
|
80
|
+
>>> config.get_tool_timeout("mypy")
|
|
81
|
+
45 # per-tool override
|
|
82
|
+
>>> config.get_tool_timeout("ruff")
|
|
83
|
+
30 # global fallback
|
|
84
|
+
"""
|
|
85
|
+
if self.tool_timeouts and tool_name in self.tool_timeouts:
|
|
86
|
+
return self.tool_timeouts[tool_name]
|
|
87
|
+
return self.timeout_seconds
|
|
88
|
+
|
|
89
|
+
@classmethod
|
|
90
|
+
def from_dict(cls, config_dict: Dict[str, Any]) -> "ToolExecutionConfig":
|
|
91
|
+
"""
|
|
92
|
+
Create config from dictionary (loaded from YAML).
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
config_dict: Configuration dictionary
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
ToolExecutionConfig instance
|
|
99
|
+
|
|
100
|
+
Example:
|
|
101
|
+
>>> config = ToolExecutionConfig.from_dict({
|
|
102
|
+
... "enabled": True,
|
|
103
|
+
... "timeout_seconds": 30
|
|
104
|
+
... })
|
|
105
|
+
"""
|
|
106
|
+
return cls(
|
|
107
|
+
enabled=config_dict.get("enabled", True),
|
|
108
|
+
timeout_seconds=config_dict.get("timeout_seconds", 30),
|
|
109
|
+
max_concurrent_tools=config_dict.get("max_concurrent_tools", 4),
|
|
110
|
+
fallback_to_sequential=config_dict.get("fallback_to_sequential", True),
|
|
111
|
+
tool_timeouts=config_dict.get("tool_timeouts"),
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class ToolStatus(str, Enum):
|
|
116
|
+
"""Status of tool execution."""
|
|
117
|
+
SUCCESS = "success"
|
|
118
|
+
TIMEOUT = "timeout"
|
|
119
|
+
ERROR = "error"
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@dataclass(frozen=True)
|
|
123
|
+
class ToolResult:
|
|
124
|
+
"""
|
|
125
|
+
Result from single tool execution.
|
|
126
|
+
|
|
127
|
+
Attributes:
|
|
128
|
+
tool: Tool name (ruff, mypy, bandit, pip-audit, jscpd)
|
|
129
|
+
status: Execution status (success, timeout, error)
|
|
130
|
+
data: Tool-specific result data (None if timeout/error)
|
|
131
|
+
duration: Execution time in seconds
|
|
132
|
+
error: Error message if status != success
|
|
133
|
+
exit_code: Tool exit code (None if timeout)
|
|
134
|
+
|
|
135
|
+
Example (success):
|
|
136
|
+
>>> result = ToolResult(
|
|
137
|
+
... tool="ruff",
|
|
138
|
+
... status=ToolStatus.SUCCESS,
|
|
139
|
+
... data={"issues": []},
|
|
140
|
+
... duration=2.3,
|
|
141
|
+
... error=None
|
|
142
|
+
... )
|
|
143
|
+
|
|
144
|
+
Example (timeout):
|
|
145
|
+
>>> result = ToolResult(
|
|
146
|
+
... tool="mypy",
|
|
147
|
+
... status=ToolStatus.TIMEOUT,
|
|
148
|
+
... data=None,
|
|
149
|
+
... duration=30.5,
|
|
150
|
+
... error="Timeout after 30.5s"
|
|
151
|
+
... )
|
|
152
|
+
|
|
153
|
+
Example (error):
|
|
154
|
+
>>> result = ToolResult(
|
|
155
|
+
... tool="bandit",
|
|
156
|
+
... status=ToolStatus.ERROR,
|
|
157
|
+
... data=None,
|
|
158
|
+
... duration=1.2,
|
|
159
|
+
... error="subprocess returned exit code 2",
|
|
160
|
+
... exit_code=2
|
|
161
|
+
... )
|
|
162
|
+
"""
|
|
163
|
+
tool: str
|
|
164
|
+
status: ToolStatus
|
|
165
|
+
data: Optional[Any]
|
|
166
|
+
duration: float
|
|
167
|
+
error: Optional[str] = None
|
|
168
|
+
exit_code: Optional[int] = None
|
|
169
|
+
|
|
170
|
+
def is_success(self) -> bool:
|
|
171
|
+
"""Check if tool executed successfully."""
|
|
172
|
+
return self.status == ToolStatus.SUCCESS
|
|
173
|
+
|
|
174
|
+
def is_timeout(self) -> bool:
|
|
175
|
+
"""Check if tool timed out."""
|
|
176
|
+
return self.status == ToolStatus.TIMEOUT
|
|
177
|
+
|
|
178
|
+
def is_error(self) -> bool:
|
|
179
|
+
"""Check if tool failed with error."""
|
|
180
|
+
return self.status == ToolStatus.ERROR
|
|
181
|
+
|
|
182
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
183
|
+
"""Convert to dictionary for serialization."""
|
|
184
|
+
return {
|
|
185
|
+
"tool": self.tool,
|
|
186
|
+
"status": self.status.value,
|
|
187
|
+
"data": self.data,
|
|
188
|
+
"duration": self.duration,
|
|
189
|
+
"error": self.error,
|
|
190
|
+
"exit_code": self.exit_code,
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
# ============================================================================
|
|
195
|
+
# Parallel Tool Executor
|
|
196
|
+
# ============================================================================
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class ParallelToolExecutor:
|
|
200
|
+
"""
|
|
201
|
+
Coordinates parallel execution of quality tools with timeout protection.
|
|
202
|
+
|
|
203
|
+
Architecture:
|
|
204
|
+
- Phase 1: Parallel batch (Ruff, mypy, bandit, pip-audit)
|
|
205
|
+
- Phase 2: Sequential batch (jscpd - requires full context)
|
|
206
|
+
- Error recovery via asyncio.gather(return_exceptions=True)
|
|
207
|
+
- Timeout protection via asyncio.wait_for()
|
|
208
|
+
|
|
209
|
+
Example:
|
|
210
|
+
>>> config = ToolExecutionConfig(enabled=True, timeout_seconds=30)
|
|
211
|
+
>>> executor = ParallelToolExecutor(config)
|
|
212
|
+
>>> results = await executor.execute_parallel(Path("src/example.py"))
|
|
213
|
+
"""
|
|
214
|
+
|
|
215
|
+
def __init__(self, config: ToolExecutionConfig):
|
|
216
|
+
"""
|
|
217
|
+
Initialize parallel tool executor.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
config: Tool execution configuration
|
|
221
|
+
"""
|
|
222
|
+
self.config = config
|
|
223
|
+
self.logger = logging.getLogger(__name__)
|
|
224
|
+
|
|
225
|
+
async def execute_parallel(
|
|
226
|
+
self,
|
|
227
|
+
file_path: Path,
|
|
228
|
+
tool_runners: Optional[Dict[str, Callable]] = None,
|
|
229
|
+
) -> List[ToolResult]:
|
|
230
|
+
"""
|
|
231
|
+
Execute all quality tools with parallel optimization.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
file_path: Path to file to review
|
|
235
|
+
tool_runners: Optional dict of tool functions (for testing/customization)
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
List of ToolResult objects (successful, timeout, or error)
|
|
239
|
+
|
|
240
|
+
Raises:
|
|
241
|
+
Never raises - always returns partial results on failures
|
|
242
|
+
|
|
243
|
+
Example:
|
|
244
|
+
>>> results = await executor.execute_parallel(Path("src/example.py"))
|
|
245
|
+
>>> successful = [r for r in results if r.is_success()]
|
|
246
|
+
>>> len(successful)
|
|
247
|
+
4 # Ruff, mypy, bandit, pip-audit succeeded
|
|
248
|
+
|
|
249
|
+
Performance:
|
|
250
|
+
- Sequential: ~23s (sum of all tools)
|
|
251
|
+
- Parallel: ~12s (2x faster)
|
|
252
|
+
"""
|
|
253
|
+
if not self.config.enabled:
|
|
254
|
+
self.logger.info("Parallel execution disabled, using sequential fallback")
|
|
255
|
+
return await self._execute_sequential(file_path, tool_runners)
|
|
256
|
+
|
|
257
|
+
try:
|
|
258
|
+
# Phase 1: Parallel batch
|
|
259
|
+
self.logger.debug("Starting parallel batch execution")
|
|
260
|
+
parallel_results = await self._execute_parallel_batch(
|
|
261
|
+
file_path, tool_runners
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
# Phase 2: Sequential batch (jscpd requires full context)
|
|
265
|
+
self.logger.debug("Starting sequential batch execution")
|
|
266
|
+
sequential_results = await self._execute_sequential_batch(
|
|
267
|
+
file_path, tool_runners
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
all_results = parallel_results + sequential_results
|
|
271
|
+
|
|
272
|
+
# Log summary
|
|
273
|
+
successful = [r for r in all_results if r.is_success()]
|
|
274
|
+
failed = [r for r in all_results if not r.is_success()]
|
|
275
|
+
total_time = sum(r.duration for r in all_results)
|
|
276
|
+
|
|
277
|
+
self.logger.info(
|
|
278
|
+
f"Parallel execution complete: {len(successful)}/{len(all_results)} "
|
|
279
|
+
f"tools successful, total time: {total_time:.2f}s"
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
if failed:
|
|
283
|
+
self.logger.warning(
|
|
284
|
+
f"{len(failed)} tools failed: "
|
|
285
|
+
f"{', '.join(r.tool for r in failed)}"
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
return all_results
|
|
289
|
+
|
|
290
|
+
except Exception as e:
|
|
291
|
+
self.logger.error(f"Parallel execution failed: {e}", exc_info=True)
|
|
292
|
+
|
|
293
|
+
if self.config.fallback_to_sequential:
|
|
294
|
+
self.logger.warning("Falling back to sequential execution")
|
|
295
|
+
return await self._execute_sequential(file_path, tool_runners)
|
|
296
|
+
|
|
297
|
+
# Return empty list on catastrophic failure (defensive programming)
|
|
298
|
+
self.logger.error("No fallback configured, returning empty results")
|
|
299
|
+
return []
|
|
300
|
+
|
|
301
|
+
async def _execute_parallel_batch(
|
|
302
|
+
self,
|
|
303
|
+
file_path: Path,
|
|
304
|
+
tool_runners: Optional[Dict[str, Callable]] = None,
|
|
305
|
+
) -> List[ToolResult]:
|
|
306
|
+
"""
|
|
307
|
+
Execute independent tools concurrently.
|
|
308
|
+
|
|
309
|
+
Args:
|
|
310
|
+
file_path: Path to file to analyze
|
|
311
|
+
tool_runners: Optional dict of tool functions
|
|
312
|
+
|
|
313
|
+
Returns:
|
|
314
|
+
List of ToolResult objects from parallel execution
|
|
315
|
+
"""
|
|
316
|
+
# Default tool runners (can be overridden for testing)
|
|
317
|
+
if tool_runners is None:
|
|
318
|
+
tool_runners = {}
|
|
319
|
+
|
|
320
|
+
# Build list of tasks for parallel execution
|
|
321
|
+
tasks = []
|
|
322
|
+
parallel_tools = ["ruff", "mypy", "bandit", "pip_audit"]
|
|
323
|
+
|
|
324
|
+
for tool_name in parallel_tools:
|
|
325
|
+
if tool_name in tool_runners:
|
|
326
|
+
tool_func = tool_runners[tool_name]
|
|
327
|
+
tasks.append(
|
|
328
|
+
self.execute_with_timeout(tool_func, file_path, tool_name)
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
if not tasks:
|
|
332
|
+
self.logger.warning("No parallel tools configured")
|
|
333
|
+
return []
|
|
334
|
+
|
|
335
|
+
# Execute all tasks concurrently
|
|
336
|
+
# return_exceptions=True: Continue on individual failures
|
|
337
|
+
results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
338
|
+
|
|
339
|
+
# Process results (convert exceptions to ToolResult objects)
|
|
340
|
+
return self._process_results(results)
|
|
341
|
+
|
|
342
|
+
async def _execute_sequential_batch(
|
|
343
|
+
self,
|
|
344
|
+
file_path: Path,
|
|
345
|
+
tool_runners: Optional[Dict[str, Callable]] = None,
|
|
346
|
+
) -> List[ToolResult]:
|
|
347
|
+
"""
|
|
348
|
+
Execute context-dependent tools sequentially.
|
|
349
|
+
|
|
350
|
+
Args:
|
|
351
|
+
file_path: Path to file to analyze
|
|
352
|
+
tool_runners: Optional dict of tool functions
|
|
353
|
+
|
|
354
|
+
Returns:
|
|
355
|
+
List of ToolResult objects from sequential execution
|
|
356
|
+
"""
|
|
357
|
+
if tool_runners is None:
|
|
358
|
+
tool_runners = {}
|
|
359
|
+
|
|
360
|
+
# jscpd requires full project context (cannot parallelize effectively)
|
|
361
|
+
if "jscpd" not in tool_runners:
|
|
362
|
+
self.logger.debug("No sequential tools configured")
|
|
363
|
+
return []
|
|
364
|
+
|
|
365
|
+
jscpd_result = await self.execute_with_timeout(
|
|
366
|
+
tool_runners["jscpd"], file_path, "jscpd"
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
return [self._process_single_result(jscpd_result)]
|
|
370
|
+
|
|
371
|
+
async def _execute_sequential(
|
|
372
|
+
self,
|
|
373
|
+
file_path: Path,
|
|
374
|
+
tool_runners: Optional[Dict[str, Callable]] = None,
|
|
375
|
+
) -> List[ToolResult]:
|
|
376
|
+
"""
|
|
377
|
+
Fallback: Execute all tools sequentially.
|
|
378
|
+
|
|
379
|
+
Args:
|
|
380
|
+
file_path: Path to file to analyze
|
|
381
|
+
tool_runners: Optional dict of tool functions
|
|
382
|
+
|
|
383
|
+
Returns:
|
|
384
|
+
List of ToolResult objects from sequential execution
|
|
385
|
+
"""
|
|
386
|
+
if tool_runners is None:
|
|
387
|
+
tool_runners = {}
|
|
388
|
+
|
|
389
|
+
results = []
|
|
390
|
+
all_tools = ["ruff", "mypy", "bandit", "pip_audit", "jscpd"]
|
|
391
|
+
|
|
392
|
+
for tool_name in all_tools:
|
|
393
|
+
if tool_name in tool_runners:
|
|
394
|
+
result = await self.execute_with_timeout(
|
|
395
|
+
tool_runners[tool_name], file_path, tool_name
|
|
396
|
+
)
|
|
397
|
+
results.append(result)
|
|
398
|
+
|
|
399
|
+
return results
|
|
400
|
+
|
|
401
|
+
async def execute_with_timeout(
|
|
402
|
+
self,
|
|
403
|
+
tool_func: Callable[[Path], Any],
|
|
404
|
+
file_path: Path,
|
|
405
|
+
tool_name: Optional[str] = None,
|
|
406
|
+
) -> ToolResult:
|
|
407
|
+
"""
|
|
408
|
+
Execute single tool with timeout protection.
|
|
409
|
+
|
|
410
|
+
Args:
|
|
411
|
+
tool_func: Async tool function to execute
|
|
412
|
+
file_path: Path to file to analyze
|
|
413
|
+
tool_name: Tool name for logging (auto-detected if None)
|
|
414
|
+
|
|
415
|
+
Returns:
|
|
416
|
+
ToolResult (success, timeout, or error)
|
|
417
|
+
|
|
418
|
+
Example:
|
|
419
|
+
>>> result = await executor.execute_with_timeout(
|
|
420
|
+
... run_ruff_async,
|
|
421
|
+
... Path("src/example.py")
|
|
422
|
+
... )
|
|
423
|
+
>>> result.status
|
|
424
|
+
ToolStatus.SUCCESS
|
|
425
|
+
>>> result.duration
|
|
426
|
+
2.3
|
|
427
|
+
|
|
428
|
+
Timeout Handling:
|
|
429
|
+
- Global timeout: config.timeout_seconds
|
|
430
|
+
- Per-tool timeout: config.get_tool_timeout(tool_name)
|
|
431
|
+
- Timeout exception → ToolResult with status=TIMEOUT
|
|
432
|
+
"""
|
|
433
|
+
# Extract tool name from function name if not provided
|
|
434
|
+
if tool_name is None:
|
|
435
|
+
tool_name = tool_func.__name__.replace("run_", "").replace("_async", "")
|
|
436
|
+
|
|
437
|
+
# Get timeout for this tool
|
|
438
|
+
timeout = self.config.get_tool_timeout(tool_name)
|
|
439
|
+
|
|
440
|
+
start_time = time.time()
|
|
441
|
+
|
|
442
|
+
try:
|
|
443
|
+
# Execute with timeout protection
|
|
444
|
+
result = await asyncio.wait_for(
|
|
445
|
+
tool_func(file_path), timeout=timeout
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
duration = time.time() - start_time
|
|
449
|
+
self.logger.debug(f"{tool_name} completed in {duration:.2f}s")
|
|
450
|
+
|
|
451
|
+
return ToolResult(
|
|
452
|
+
tool=tool_name,
|
|
453
|
+
status=ToolStatus.SUCCESS,
|
|
454
|
+
data=result,
|
|
455
|
+
duration=duration,
|
|
456
|
+
error=None,
|
|
457
|
+
exit_code=None,
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
except asyncio.TimeoutError:
|
|
461
|
+
duration = time.time() - start_time
|
|
462
|
+
error_msg = f"Timeout after {duration:.2f}s (limit: {timeout}s)"
|
|
463
|
+
|
|
464
|
+
self.logger.warning(f"{tool_name}: {error_msg}")
|
|
465
|
+
|
|
466
|
+
return self._handle_timeout(tool_name, duration, timeout)
|
|
467
|
+
|
|
468
|
+
except Exception as e:
|
|
469
|
+
duration = time.time() - start_time
|
|
470
|
+
self.logger.error(
|
|
471
|
+
f"{tool_name} failed after {duration:.2f}s: {e}",
|
|
472
|
+
exc_info=True,
|
|
473
|
+
)
|
|
474
|
+
|
|
475
|
+
return self._handle_error(tool_name, e, duration)
|
|
476
|
+
|
|
477
|
+
def _handle_timeout(
|
|
478
|
+
self, tool_name: str, duration: float, timeout: int
|
|
479
|
+
) -> ToolResult:
|
|
480
|
+
"""
|
|
481
|
+
Create timeout result annotation.
|
|
482
|
+
|
|
483
|
+
Args:
|
|
484
|
+
tool_name: Name of the tool that timed out
|
|
485
|
+
duration: Actual execution time
|
|
486
|
+
timeout: Configured timeout limit
|
|
487
|
+
|
|
488
|
+
Returns:
|
|
489
|
+
ToolResult with status=TIMEOUT
|
|
490
|
+
"""
|
|
491
|
+
return ToolResult(
|
|
492
|
+
tool=tool_name,
|
|
493
|
+
status=ToolStatus.TIMEOUT,
|
|
494
|
+
data=None,
|
|
495
|
+
duration=duration,
|
|
496
|
+
error=f"Timeout after {duration:.2f}s (limit: {timeout}s)",
|
|
497
|
+
exit_code=None,
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
def _handle_error(
|
|
501
|
+
self, tool_name: str, exception: Exception, duration: float
|
|
502
|
+
) -> ToolResult:
|
|
503
|
+
"""
|
|
504
|
+
Create error result annotation.
|
|
505
|
+
|
|
506
|
+
Args:
|
|
507
|
+
tool_name: Name of the tool that failed
|
|
508
|
+
exception: Exception that was raised
|
|
509
|
+
duration: Execution time before failure
|
|
510
|
+
|
|
511
|
+
Returns:
|
|
512
|
+
ToolResult with status=ERROR
|
|
513
|
+
"""
|
|
514
|
+
# Extract exit code from subprocess errors
|
|
515
|
+
exit_code = None
|
|
516
|
+
if hasattr(exception, "returncode"):
|
|
517
|
+
exit_code = exception.returncode
|
|
518
|
+
|
|
519
|
+
return ToolResult(
|
|
520
|
+
tool=tool_name,
|
|
521
|
+
status=ToolStatus.ERROR,
|
|
522
|
+
data=None,
|
|
523
|
+
duration=duration,
|
|
524
|
+
error=str(exception),
|
|
525
|
+
exit_code=exit_code,
|
|
526
|
+
)
|
|
527
|
+
|
|
528
|
+
def _process_results(
|
|
529
|
+
self, results: List[Union[ToolResult, Exception]]
|
|
530
|
+
) -> List[ToolResult]:
|
|
531
|
+
"""
|
|
532
|
+
Convert exceptions to error ToolResults.
|
|
533
|
+
|
|
534
|
+
Args:
|
|
535
|
+
results: List of ToolResult or Exception objects
|
|
536
|
+
|
|
537
|
+
Returns:
|
|
538
|
+
List of ToolResult objects (exceptions converted to errors)
|
|
539
|
+
"""
|
|
540
|
+
processed = []
|
|
541
|
+
|
|
542
|
+
for result in results:
|
|
543
|
+
if isinstance(result, Exception):
|
|
544
|
+
# Exception from asyncio.gather (shouldn't happen with our error handling)
|
|
545
|
+
self.logger.error(f"Unexpected exception in gather: {result}")
|
|
546
|
+
processed.append(
|
|
547
|
+
self._handle_error("unknown", result, 0.0)
|
|
548
|
+
)
|
|
549
|
+
elif isinstance(result, ToolResult):
|
|
550
|
+
processed.append(result)
|
|
551
|
+
else:
|
|
552
|
+
# Unexpected result type (defensive programming)
|
|
553
|
+
self.logger.error(f"Unexpected result type: {type(result)}")
|
|
554
|
+
processed.append(
|
|
555
|
+
ToolResult(
|
|
556
|
+
tool="unknown",
|
|
557
|
+
status=ToolStatus.ERROR,
|
|
558
|
+
data=None,
|
|
559
|
+
duration=0.0,
|
|
560
|
+
error=f"Unexpected result type: {type(result)}",
|
|
561
|
+
exit_code=None,
|
|
562
|
+
)
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
return processed
|
|
566
|
+
|
|
567
|
+
def _process_single_result(
|
|
568
|
+
self, result: Union[ToolResult, Exception]
|
|
569
|
+
) -> ToolResult:
|
|
570
|
+
"""
|
|
571
|
+
Process single result (for sequential execution).
|
|
572
|
+
|
|
573
|
+
Args:
|
|
574
|
+
result: ToolResult or Exception
|
|
575
|
+
|
|
576
|
+
Returns:
|
|
577
|
+
ToolResult object
|
|
578
|
+
"""
|
|
579
|
+
if isinstance(result, Exception):
|
|
580
|
+
return self._handle_error("unknown", result, 0.0)
|
|
581
|
+
return result
|