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,445 +1,445 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Git operations utility module.
|
|
3
|
-
|
|
4
|
-
Provides functions for git commit, push, and branch operations.
|
|
5
|
-
Used by bug fix agent for automatic commit functionality.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
import logging
|
|
9
|
-
import os
|
|
10
|
-
import shutil
|
|
11
|
-
import subprocess
|
|
12
|
-
import sys
|
|
13
|
-
from datetime import datetime, UTC
|
|
14
|
-
from pathlib import Path
|
|
15
|
-
from typing import Any
|
|
16
|
-
|
|
17
|
-
logger = logging.getLogger(__name__)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def _ensure_utf8_encoding() -> None:
|
|
21
|
-
"""Ensure UTF-8 encoding for Windows console output."""
|
|
22
|
-
if sys.platform == "win32":
|
|
23
|
-
os.environ["PYTHONIOENCODING"] = "utf-8"
|
|
24
|
-
try:
|
|
25
|
-
sys.stdout.reconfigure(encoding="utf-8")
|
|
26
|
-
sys.stderr.reconfigure(encoding="utf-8")
|
|
27
|
-
except AttributeError:
|
|
28
|
-
# Python < 3.7 - use environment variable only
|
|
29
|
-
pass
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def _get_git_path() -> str:
|
|
33
|
-
"""Get git executable path."""
|
|
34
|
-
return shutil.which("git") or "git"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def _run_git_command(
|
|
38
|
-
args: list[str],
|
|
39
|
-
cwd: Path | None = None,
|
|
40
|
-
capture_output: bool = True,
|
|
41
|
-
check: bool = False,
|
|
42
|
-
) -> subprocess.CompletedProcess[str]:
|
|
43
|
-
"""
|
|
44
|
-
Run a git command with proper encoding.
|
|
45
|
-
|
|
46
|
-
Args:
|
|
47
|
-
args: Git command arguments (e.g., ['status', '--porcelain'])
|
|
48
|
-
cwd: Working directory (default: current directory)
|
|
49
|
-
capture_output: Whether to capture stdout/stderr
|
|
50
|
-
check: Whether to raise on non-zero exit code
|
|
51
|
-
|
|
52
|
-
Returns:
|
|
53
|
-
CompletedProcess with stdout/stderr
|
|
54
|
-
"""
|
|
55
|
-
_ensure_utf8_encoding()
|
|
56
|
-
git_path = _get_git_path()
|
|
57
|
-
cwd = cwd or Path.cwd()
|
|
58
|
-
|
|
59
|
-
try:
|
|
60
|
-
result = subprocess.run(
|
|
61
|
-
[git_path] + args,
|
|
62
|
-
cwd=cwd,
|
|
63
|
-
capture_output=capture_output,
|
|
64
|
-
text=True,
|
|
65
|
-
encoding="utf-8",
|
|
66
|
-
errors="replace",
|
|
67
|
-
check=check,
|
|
68
|
-
)
|
|
69
|
-
return result
|
|
70
|
-
except subprocess.CalledProcessError as e:
|
|
71
|
-
error_msg = e.stderr or str(e) if e.stderr else str(e)
|
|
72
|
-
logger.error(f"Git command failed: {' '.join([git_path] + args)}\nError: {error_msg}")
|
|
73
|
-
raise
|
|
74
|
-
except FileNotFoundError:
|
|
75
|
-
raise RuntimeError("Git is not installed or not found in PATH")
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def check_git_available() -> bool:
|
|
79
|
-
"""Check if Git is available."""
|
|
80
|
-
return shutil.which("git") is not None
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def is_git_repository(path: Path | None = None) -> bool:
|
|
84
|
-
"""Check if path is a git repository."""
|
|
85
|
-
path = path or Path.cwd()
|
|
86
|
-
return (path / ".git").exists() or (path / ".git").is_dir()
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def get_current_branch(path: Path | None = None) -> str:
|
|
90
|
-
"""Get the current git branch name."""
|
|
91
|
-
path = path or Path.cwd()
|
|
92
|
-
result = _run_git_command(["rev-parse", "--abbrev-ref", "HEAD"], cwd=path, check=True)
|
|
93
|
-
return result.stdout.strip()
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def check_uncommitted_changes(path: Path | None = None) -> list[str]:
|
|
97
|
-
"""Check for uncommitted changes."""
|
|
98
|
-
path = path or Path.cwd()
|
|
99
|
-
result = _run_git_command(["status", "--porcelain"], cwd=path, check=True)
|
|
100
|
-
lines = result.stdout.strip().split("\n")
|
|
101
|
-
return [line for line in lines if line.strip()]
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def create_and_checkout_branch(branch_name: str, path: Path | None = None) -> dict[str, Any]:
|
|
105
|
-
"""
|
|
106
|
-
Create a new branch and check it out.
|
|
107
|
-
|
|
108
|
-
Args:
|
|
109
|
-
branch_name: Name of the new branch
|
|
110
|
-
path: Path to git repository (default: current directory)
|
|
111
|
-
|
|
112
|
-
Returns:
|
|
113
|
-
Dictionary with success status and branch name
|
|
114
|
-
"""
|
|
115
|
-
path = path or Path.cwd()
|
|
116
|
-
|
|
117
|
-
if not check_git_available():
|
|
118
|
-
raise RuntimeError("Git is not installed or not in PATH")
|
|
119
|
-
|
|
120
|
-
if not is_git_repository(path):
|
|
121
|
-
raise RuntimeError(f"Not a git repository: {path}")
|
|
122
|
-
|
|
123
|
-
try:
|
|
124
|
-
# Create and checkout branch
|
|
125
|
-
_run_git_command(["checkout", "-b", branch_name], cwd=path, check=True)
|
|
126
|
-
logger.info(f"Created and checked out branch: {branch_name}")
|
|
127
|
-
return {"success": True, "branch": branch_name, "error": None}
|
|
128
|
-
except subprocess.CalledProcessError as e:
|
|
129
|
-
error_msg = e.stderr or str(e) if e.stderr else str(e)
|
|
130
|
-
logger.error(f"Failed to create branch: {error_msg}")
|
|
131
|
-
# If branch already exists, try to checkout (plan 2.3 branch-for-agent-changes)
|
|
132
|
-
if "already exists" in error_msg.lower():
|
|
133
|
-
try:
|
|
134
|
-
_run_git_command(["checkout", branch_name], cwd=path, check=True)
|
|
135
|
-
logger.info(f"Checked out existing branch: {branch_name}")
|
|
136
|
-
return {"success": True, "branch": branch_name, "error": None}
|
|
137
|
-
except subprocess.CalledProcessError:
|
|
138
|
-
pass
|
|
139
|
-
return {"success": False, "branch": branch_name, "error": error_msg}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
def checkout_branch(branch_name: str, path: Path | None = None) -> dict[str, Any]:
|
|
143
|
-
"""
|
|
144
|
-
Check out an existing branch.
|
|
145
|
-
|
|
146
|
-
Args:
|
|
147
|
-
branch_name: Name of the branch
|
|
148
|
-
path: Path to git repository (default: current directory)
|
|
149
|
-
|
|
150
|
-
Returns:
|
|
151
|
-
Dictionary with success status and branch name
|
|
152
|
-
"""
|
|
153
|
-
path = path or Path.cwd()
|
|
154
|
-
if not check_git_available() or not is_git_repository(path):
|
|
155
|
-
return {"success": False, "branch": branch_name, "error": "Git not available or not a repository"}
|
|
156
|
-
try:
|
|
157
|
-
_run_git_command(["checkout", branch_name], cwd=path, check=True)
|
|
158
|
-
return {"success": True, "branch": branch_name, "error": None}
|
|
159
|
-
except subprocess.CalledProcessError as e:
|
|
160
|
-
return {"success": False, "branch": branch_name, "error": e.stderr or str(e)}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
def commit_changes(
|
|
164
|
-
message: str,
|
|
165
|
-
files: list[str] | None = None,
|
|
166
|
-
branch: str = "main",
|
|
167
|
-
path: Path | None = None,
|
|
168
|
-
) -> dict[str, Any]:
|
|
169
|
-
"""
|
|
170
|
-
Commit changes to git repository.
|
|
171
|
-
|
|
172
|
-
Args:
|
|
173
|
-
message: Commit message
|
|
174
|
-
files: List of specific files to commit (None = all changes)
|
|
175
|
-
branch: Target branch (default: "main")
|
|
176
|
-
path: Path to git repository (default: current directory)
|
|
177
|
-
|
|
178
|
-
Returns:
|
|
179
|
-
Dictionary with:
|
|
180
|
-
- success: bool
|
|
181
|
-
- commit_hash: str | None
|
|
182
|
-
- branch: str
|
|
183
|
-
- error: str | None
|
|
184
|
-
|
|
185
|
-
Raises:
|
|
186
|
-
RuntimeError: If git is not available or not in a repository
|
|
187
|
-
ValueError: If not on the specified branch and branch is not "main"
|
|
188
|
-
"""
|
|
189
|
-
path = path or Path.cwd()
|
|
190
|
-
|
|
191
|
-
# Validate git availability
|
|
192
|
-
if not check_git_available():
|
|
193
|
-
raise RuntimeError("Git is not installed or not in PATH")
|
|
194
|
-
|
|
195
|
-
# Validate git repository
|
|
196
|
-
if not is_git_repository(path):
|
|
197
|
-
raise RuntimeError(f"Not a git repository: {path}")
|
|
198
|
-
|
|
199
|
-
# Check current branch
|
|
200
|
-
current_branch = get_current_branch(path)
|
|
201
|
-
if branch != "main" and current_branch != branch:
|
|
202
|
-
logger.warning(
|
|
203
|
-
f"Current branch is '{current_branch}', but target branch is '{branch}'"
|
|
204
|
-
)
|
|
205
|
-
elif current_branch != branch:
|
|
206
|
-
logger.warning(
|
|
207
|
-
f"Current branch is '{current_branch}', committing to '{branch}'"
|
|
208
|
-
)
|
|
209
|
-
|
|
210
|
-
# Check for changes
|
|
211
|
-
uncommitted = check_uncommitted_changes(path)
|
|
212
|
-
if not uncommitted:
|
|
213
|
-
return {
|
|
214
|
-
"success": False,
|
|
215
|
-
"commit_hash": None,
|
|
216
|
-
"branch": current_branch,
|
|
217
|
-
"error": "No uncommitted changes to commit",
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
try:
|
|
221
|
-
# Stage files
|
|
222
|
-
if files:
|
|
223
|
-
# Stage specific files
|
|
224
|
-
for file_path in files:
|
|
225
|
-
file_full_path = path / file_path
|
|
226
|
-
if file_full_path.exists():
|
|
227
|
-
_run_git_command(["add", file_path], cwd=path, check=True)
|
|
228
|
-
else:
|
|
229
|
-
logger.warning(f"File not found, skipping: {file_path}")
|
|
230
|
-
else:
|
|
231
|
-
# Stage all changes
|
|
232
|
-
_run_git_command(["add", "-A"], cwd=path, check=True)
|
|
233
|
-
|
|
234
|
-
# Commit
|
|
235
|
-
commit_result = _run_git_command(
|
|
236
|
-
["commit", "-m", message], cwd=path, check=True
|
|
237
|
-
)
|
|
238
|
-
|
|
239
|
-
# Get commit hash
|
|
240
|
-
hash_result = _run_git_command(
|
|
241
|
-
["rev-parse", "HEAD"], cwd=path, check=True
|
|
242
|
-
)
|
|
243
|
-
commit_hash = hash_result.stdout.strip()
|
|
244
|
-
|
|
245
|
-
logger.info(
|
|
246
|
-
f"Committed changes to branch '{current_branch}': {commit_hash[:8]}"
|
|
247
|
-
)
|
|
248
|
-
|
|
249
|
-
return {
|
|
250
|
-
"success": True,
|
|
251
|
-
"commit_hash": commit_hash,
|
|
252
|
-
"branch": current_branch,
|
|
253
|
-
"error": None,
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
except subprocess.CalledProcessError as e:
|
|
257
|
-
error_msg = e.stderr or str(e) if e.stderr else str(e)
|
|
258
|
-
logger.error(f"Failed to commit changes: {error_msg}")
|
|
259
|
-
return {
|
|
260
|
-
"success": False,
|
|
261
|
-
"commit_hash": None,
|
|
262
|
-
"branch": current_branch,
|
|
263
|
-
"error": error_msg,
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
def push_changes(
|
|
268
|
-
branch: str = "main",
|
|
269
|
-
path: Path | None = None,
|
|
270
|
-
force: bool = False,
|
|
271
|
-
) -> dict[str, Any]:
|
|
272
|
-
"""
|
|
273
|
-
Push changes to remote repository.
|
|
274
|
-
|
|
275
|
-
Args:
|
|
276
|
-
branch: Branch to push (default: "main")
|
|
277
|
-
path: Path to git repository (default: current directory)
|
|
278
|
-
force: Whether to force push (default: False, NEVER use True for main)
|
|
279
|
-
|
|
280
|
-
Returns:
|
|
281
|
-
Dictionary with:
|
|
282
|
-
- success: bool
|
|
283
|
-
- branch: str
|
|
284
|
-
- error: str | None
|
|
285
|
-
|
|
286
|
-
Raises:
|
|
287
|
-
RuntimeError: If git is not available or not in a repository
|
|
288
|
-
ValueError: If force=True and branch is "main"
|
|
289
|
-
"""
|
|
290
|
-
path = path or Path.cwd()
|
|
291
|
-
|
|
292
|
-
# Safety check: never force push to main
|
|
293
|
-
if force and branch == "main":
|
|
294
|
-
raise ValueError("Cannot force push to main branch")
|
|
295
|
-
|
|
296
|
-
# Plan 3.3: no_direct_push_main from policies.yaml
|
|
297
|
-
if branch == "main":
|
|
298
|
-
try:
|
|
299
|
-
from .policy_loader import load_policies
|
|
300
|
-
p = load_policies(path)
|
|
301
|
-
if p and p.branch_protection and getattr(p.branch_protection, "no_direct_push_main", True):
|
|
302
|
-
raise ValueError(
|
|
303
|
-
"Direct push to main is disallowed by .tapps-agents/policies.yaml (branch_protection.no_direct_push_main). "
|
|
304
|
-
"Create a branch and merge via PR, or adjust policies."
|
|
305
|
-
)
|
|
306
|
-
except ValueError:
|
|
307
|
-
raise
|
|
308
|
-
except Exception: # pylint: disable=broad-except
|
|
309
|
-
pass # if policy load fails, allow push
|
|
310
|
-
|
|
311
|
-
# Validate git availability
|
|
312
|
-
if not check_git_available():
|
|
313
|
-
raise RuntimeError("Git is not installed or not in PATH")
|
|
314
|
-
|
|
315
|
-
# Validate git repository
|
|
316
|
-
if not is_git_repository(path):
|
|
317
|
-
raise RuntimeError(f"Not a git repository: {path}")
|
|
318
|
-
|
|
319
|
-
try:
|
|
320
|
-
# Build push command
|
|
321
|
-
push_args = ["push"]
|
|
322
|
-
if force:
|
|
323
|
-
push_args.append("--force")
|
|
324
|
-
push_args.extend(["origin", branch])
|
|
325
|
-
|
|
326
|
-
push_result = _run_git_command(push_args, cwd=path, check=True)
|
|
327
|
-
|
|
328
|
-
logger.info(f"Pushed changes to remote branch '{branch}'")
|
|
329
|
-
|
|
330
|
-
return {
|
|
331
|
-
"success": True,
|
|
332
|
-
"branch": branch,
|
|
333
|
-
"error": None,
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
except subprocess.CalledProcessError as e:
|
|
337
|
-
error_msg = e.stderr or str(e) if e.stderr else str(e)
|
|
338
|
-
logger.error(f"Failed to push changes: {error_msg}")
|
|
339
|
-
return {
|
|
340
|
-
"success": False,
|
|
341
|
-
"branch": branch,
|
|
342
|
-
"error": error_msg,
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
def create_pull_request(
|
|
347
|
-
title: str,
|
|
348
|
-
body: str,
|
|
349
|
-
head_branch: str,
|
|
350
|
-
base_branch: str = "main",
|
|
351
|
-
path: Path | None = None,
|
|
352
|
-
) -> dict[str, Any]:
|
|
353
|
-
"""
|
|
354
|
-
Create a pull request using GitHub CLI (gh) if available.
|
|
355
|
-
|
|
356
|
-
Args:
|
|
357
|
-
title: PR title
|
|
358
|
-
body: PR body/description
|
|
359
|
-
head_branch: Source branch (branch with changes)
|
|
360
|
-
base_branch: Target branch (default: "main")
|
|
361
|
-
path: Path to git repository (default: current directory)
|
|
362
|
-
|
|
363
|
-
Returns:
|
|
364
|
-
Dictionary with:
|
|
365
|
-
- success: bool
|
|
366
|
-
- pr_url: str | None
|
|
367
|
-
- pr_number: int | None
|
|
368
|
-
- error: str | None
|
|
369
|
-
"""
|
|
370
|
-
path = path or Path.cwd()
|
|
371
|
-
|
|
372
|
-
# Check if GitHub CLI is available
|
|
373
|
-
gh_path = shutil.which("gh")
|
|
374
|
-
if not gh_path:
|
|
375
|
-
logger.warning(
|
|
376
|
-
"GitHub CLI (gh) not found. PR creation requires 'gh' to be installed and authenticated."
|
|
377
|
-
)
|
|
378
|
-
return {
|
|
379
|
-
"success": False,
|
|
380
|
-
"pr_url": None,
|
|
381
|
-
"pr_number": None,
|
|
382
|
-
"error": "GitHub CLI (gh) not found. Install from https://cli.github.com/",
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
try:
|
|
386
|
-
# Create PR using GitHub CLI
|
|
387
|
-
pr_args = [
|
|
388
|
-
"pr",
|
|
389
|
-
"create",
|
|
390
|
-
"--title", title,
|
|
391
|
-
"--body", body,
|
|
392
|
-
"--head", head_branch,
|
|
393
|
-
"--base", base_branch,
|
|
394
|
-
]
|
|
395
|
-
|
|
396
|
-
result = subprocess.run(
|
|
397
|
-
[gh_path] + pr_args,
|
|
398
|
-
cwd=path,
|
|
399
|
-
capture_output=True,
|
|
400
|
-
text=True,
|
|
401
|
-
encoding="utf-8",
|
|
402
|
-
errors="replace",
|
|
403
|
-
check=True,
|
|
404
|
-
)
|
|
405
|
-
|
|
406
|
-
# Parse PR URL from output (gh outputs the PR URL)
|
|
407
|
-
output = result.stdout.strip()
|
|
408
|
-
pr_url = output if output.startswith("http") else None
|
|
409
|
-
|
|
410
|
-
# Try to extract PR number from URL
|
|
411
|
-
pr_number = None
|
|
412
|
-
if pr_url:
|
|
413
|
-
# URL format: https://github.com/owner/repo/pull/123
|
|
414
|
-
parts = pr_url.split("/")
|
|
415
|
-
if "pull" in parts:
|
|
416
|
-
try:
|
|
417
|
-
pr_number = int(parts[parts.index("pull") + 1])
|
|
418
|
-
except (ValueError, IndexError):
|
|
419
|
-
pass
|
|
420
|
-
|
|
421
|
-
logger.info(f"Created pull request: {pr_url or output}")
|
|
422
|
-
|
|
423
|
-
return {
|
|
424
|
-
"success": True,
|
|
425
|
-
"pr_url": pr_url,
|
|
426
|
-
"pr_number": pr_number,
|
|
427
|
-
"error": None,
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
except subprocess.CalledProcessError as e:
|
|
431
|
-
error_msg = e.stderr or str(e) if e.stderr else str(e)
|
|
432
|
-
logger.error(f"Failed to create pull request: {error_msg}")
|
|
433
|
-
return {
|
|
434
|
-
"success": False,
|
|
435
|
-
"pr_url": None,
|
|
436
|
-
"pr_number": None,
|
|
437
|
-
"error": error_msg,
|
|
438
|
-
}
|
|
439
|
-
except FileNotFoundError:
|
|
440
|
-
return {
|
|
441
|
-
"success": False,
|
|
442
|
-
"pr_url": None,
|
|
443
|
-
"pr_number": None,
|
|
444
|
-
"error": "GitHub CLI (gh) not found",
|
|
445
|
-
}
|
|
1
|
+
"""
|
|
2
|
+
Git operations utility module.
|
|
3
|
+
|
|
4
|
+
Provides functions for git commit, push, and branch operations.
|
|
5
|
+
Used by bug fix agent for automatic commit functionality.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import os
|
|
10
|
+
import shutil
|
|
11
|
+
import subprocess
|
|
12
|
+
import sys
|
|
13
|
+
from datetime import datetime, UTC
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _ensure_utf8_encoding() -> None:
|
|
21
|
+
"""Ensure UTF-8 encoding for Windows console output."""
|
|
22
|
+
if sys.platform == "win32":
|
|
23
|
+
os.environ["PYTHONIOENCODING"] = "utf-8"
|
|
24
|
+
try:
|
|
25
|
+
sys.stdout.reconfigure(encoding="utf-8")
|
|
26
|
+
sys.stderr.reconfigure(encoding="utf-8")
|
|
27
|
+
except AttributeError:
|
|
28
|
+
# Python < 3.7 - use environment variable only
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _get_git_path() -> str:
|
|
33
|
+
"""Get git executable path."""
|
|
34
|
+
return shutil.which("git") or "git"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _run_git_command(
|
|
38
|
+
args: list[str],
|
|
39
|
+
cwd: Path | None = None,
|
|
40
|
+
capture_output: bool = True,
|
|
41
|
+
check: bool = False,
|
|
42
|
+
) -> subprocess.CompletedProcess[str]:
|
|
43
|
+
"""
|
|
44
|
+
Run a git command with proper encoding.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
args: Git command arguments (e.g., ['status', '--porcelain'])
|
|
48
|
+
cwd: Working directory (default: current directory)
|
|
49
|
+
capture_output: Whether to capture stdout/stderr
|
|
50
|
+
check: Whether to raise on non-zero exit code
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
CompletedProcess with stdout/stderr
|
|
54
|
+
"""
|
|
55
|
+
_ensure_utf8_encoding()
|
|
56
|
+
git_path = _get_git_path()
|
|
57
|
+
cwd = cwd or Path.cwd()
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
result = subprocess.run(
|
|
61
|
+
[git_path] + args,
|
|
62
|
+
cwd=cwd,
|
|
63
|
+
capture_output=capture_output,
|
|
64
|
+
text=True,
|
|
65
|
+
encoding="utf-8",
|
|
66
|
+
errors="replace",
|
|
67
|
+
check=check,
|
|
68
|
+
)
|
|
69
|
+
return result
|
|
70
|
+
except subprocess.CalledProcessError as e:
|
|
71
|
+
error_msg = e.stderr or str(e) if e.stderr else str(e)
|
|
72
|
+
logger.error(f"Git command failed: {' '.join([git_path] + args)}\nError: {error_msg}")
|
|
73
|
+
raise
|
|
74
|
+
except FileNotFoundError:
|
|
75
|
+
raise RuntimeError("Git is not installed or not found in PATH")
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def check_git_available() -> bool:
|
|
79
|
+
"""Check if Git is available."""
|
|
80
|
+
return shutil.which("git") is not None
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def is_git_repository(path: Path | None = None) -> bool:
|
|
84
|
+
"""Check if path is a git repository."""
|
|
85
|
+
path = path or Path.cwd()
|
|
86
|
+
return (path / ".git").exists() or (path / ".git").is_dir()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def get_current_branch(path: Path | None = None) -> str:
|
|
90
|
+
"""Get the current git branch name."""
|
|
91
|
+
path = path or Path.cwd()
|
|
92
|
+
result = _run_git_command(["rev-parse", "--abbrev-ref", "HEAD"], cwd=path, check=True)
|
|
93
|
+
return result.stdout.strip()
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def check_uncommitted_changes(path: Path | None = None) -> list[str]:
|
|
97
|
+
"""Check for uncommitted changes."""
|
|
98
|
+
path = path or Path.cwd()
|
|
99
|
+
result = _run_git_command(["status", "--porcelain"], cwd=path, check=True)
|
|
100
|
+
lines = result.stdout.strip().split("\n")
|
|
101
|
+
return [line for line in lines if line.strip()]
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def create_and_checkout_branch(branch_name: str, path: Path | None = None) -> dict[str, Any]:
|
|
105
|
+
"""
|
|
106
|
+
Create a new branch and check it out.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
branch_name: Name of the new branch
|
|
110
|
+
path: Path to git repository (default: current directory)
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Dictionary with success status and branch name
|
|
114
|
+
"""
|
|
115
|
+
path = path or Path.cwd()
|
|
116
|
+
|
|
117
|
+
if not check_git_available():
|
|
118
|
+
raise RuntimeError("Git is not installed or not in PATH")
|
|
119
|
+
|
|
120
|
+
if not is_git_repository(path):
|
|
121
|
+
raise RuntimeError(f"Not a git repository: {path}")
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
# Create and checkout branch
|
|
125
|
+
_run_git_command(["checkout", "-b", branch_name], cwd=path, check=True)
|
|
126
|
+
logger.info(f"Created and checked out branch: {branch_name}")
|
|
127
|
+
return {"success": True, "branch": branch_name, "error": None}
|
|
128
|
+
except subprocess.CalledProcessError as e:
|
|
129
|
+
error_msg = e.stderr or str(e) if e.stderr else str(e)
|
|
130
|
+
logger.error(f"Failed to create branch: {error_msg}")
|
|
131
|
+
# If branch already exists, try to checkout (plan 2.3 branch-for-agent-changes)
|
|
132
|
+
if "already exists" in error_msg.lower():
|
|
133
|
+
try:
|
|
134
|
+
_run_git_command(["checkout", branch_name], cwd=path, check=True)
|
|
135
|
+
logger.info(f"Checked out existing branch: {branch_name}")
|
|
136
|
+
return {"success": True, "branch": branch_name, "error": None}
|
|
137
|
+
except subprocess.CalledProcessError:
|
|
138
|
+
pass
|
|
139
|
+
return {"success": False, "branch": branch_name, "error": error_msg}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def checkout_branch(branch_name: str, path: Path | None = None) -> dict[str, Any]:
|
|
143
|
+
"""
|
|
144
|
+
Check out an existing branch.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
branch_name: Name of the branch
|
|
148
|
+
path: Path to git repository (default: current directory)
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
Dictionary with success status and branch name
|
|
152
|
+
"""
|
|
153
|
+
path = path or Path.cwd()
|
|
154
|
+
if not check_git_available() or not is_git_repository(path):
|
|
155
|
+
return {"success": False, "branch": branch_name, "error": "Git not available or not a repository"}
|
|
156
|
+
try:
|
|
157
|
+
_run_git_command(["checkout", branch_name], cwd=path, check=True)
|
|
158
|
+
return {"success": True, "branch": branch_name, "error": None}
|
|
159
|
+
except subprocess.CalledProcessError as e:
|
|
160
|
+
return {"success": False, "branch": branch_name, "error": e.stderr or str(e)}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def commit_changes(
|
|
164
|
+
message: str,
|
|
165
|
+
files: list[str] | None = None,
|
|
166
|
+
branch: str = "main",
|
|
167
|
+
path: Path | None = None,
|
|
168
|
+
) -> dict[str, Any]:
|
|
169
|
+
"""
|
|
170
|
+
Commit changes to git repository.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
message: Commit message
|
|
174
|
+
files: List of specific files to commit (None = all changes)
|
|
175
|
+
branch: Target branch (default: "main")
|
|
176
|
+
path: Path to git repository (default: current directory)
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
Dictionary with:
|
|
180
|
+
- success: bool
|
|
181
|
+
- commit_hash: str | None
|
|
182
|
+
- branch: str
|
|
183
|
+
- error: str | None
|
|
184
|
+
|
|
185
|
+
Raises:
|
|
186
|
+
RuntimeError: If git is not available or not in a repository
|
|
187
|
+
ValueError: If not on the specified branch and branch is not "main"
|
|
188
|
+
"""
|
|
189
|
+
path = path or Path.cwd()
|
|
190
|
+
|
|
191
|
+
# Validate git availability
|
|
192
|
+
if not check_git_available():
|
|
193
|
+
raise RuntimeError("Git is not installed or not in PATH")
|
|
194
|
+
|
|
195
|
+
# Validate git repository
|
|
196
|
+
if not is_git_repository(path):
|
|
197
|
+
raise RuntimeError(f"Not a git repository: {path}")
|
|
198
|
+
|
|
199
|
+
# Check current branch
|
|
200
|
+
current_branch = get_current_branch(path)
|
|
201
|
+
if branch != "main" and current_branch != branch:
|
|
202
|
+
logger.warning(
|
|
203
|
+
f"Current branch is '{current_branch}', but target branch is '{branch}'"
|
|
204
|
+
)
|
|
205
|
+
elif current_branch != branch:
|
|
206
|
+
logger.warning(
|
|
207
|
+
f"Current branch is '{current_branch}', committing to '{branch}'"
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
# Check for changes
|
|
211
|
+
uncommitted = check_uncommitted_changes(path)
|
|
212
|
+
if not uncommitted:
|
|
213
|
+
return {
|
|
214
|
+
"success": False,
|
|
215
|
+
"commit_hash": None,
|
|
216
|
+
"branch": current_branch,
|
|
217
|
+
"error": "No uncommitted changes to commit",
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
try:
|
|
221
|
+
# Stage files
|
|
222
|
+
if files:
|
|
223
|
+
# Stage specific files
|
|
224
|
+
for file_path in files:
|
|
225
|
+
file_full_path = path / file_path
|
|
226
|
+
if file_full_path.exists():
|
|
227
|
+
_run_git_command(["add", file_path], cwd=path, check=True)
|
|
228
|
+
else:
|
|
229
|
+
logger.warning(f"File not found, skipping: {file_path}")
|
|
230
|
+
else:
|
|
231
|
+
# Stage all changes
|
|
232
|
+
_run_git_command(["add", "-A"], cwd=path, check=True)
|
|
233
|
+
|
|
234
|
+
# Commit
|
|
235
|
+
commit_result = _run_git_command(
|
|
236
|
+
["commit", "-m", message], cwd=path, check=True
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
# Get commit hash
|
|
240
|
+
hash_result = _run_git_command(
|
|
241
|
+
["rev-parse", "HEAD"], cwd=path, check=True
|
|
242
|
+
)
|
|
243
|
+
commit_hash = hash_result.stdout.strip()
|
|
244
|
+
|
|
245
|
+
logger.info(
|
|
246
|
+
f"Committed changes to branch '{current_branch}': {commit_hash[:8]}"
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
"success": True,
|
|
251
|
+
"commit_hash": commit_hash,
|
|
252
|
+
"branch": current_branch,
|
|
253
|
+
"error": None,
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
except subprocess.CalledProcessError as e:
|
|
257
|
+
error_msg = e.stderr or str(e) if e.stderr else str(e)
|
|
258
|
+
logger.error(f"Failed to commit changes: {error_msg}")
|
|
259
|
+
return {
|
|
260
|
+
"success": False,
|
|
261
|
+
"commit_hash": None,
|
|
262
|
+
"branch": current_branch,
|
|
263
|
+
"error": error_msg,
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def push_changes(
|
|
268
|
+
branch: str = "main",
|
|
269
|
+
path: Path | None = None,
|
|
270
|
+
force: bool = False,
|
|
271
|
+
) -> dict[str, Any]:
|
|
272
|
+
"""
|
|
273
|
+
Push changes to remote repository.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
branch: Branch to push (default: "main")
|
|
277
|
+
path: Path to git repository (default: current directory)
|
|
278
|
+
force: Whether to force push (default: False, NEVER use True for main)
|
|
279
|
+
|
|
280
|
+
Returns:
|
|
281
|
+
Dictionary with:
|
|
282
|
+
- success: bool
|
|
283
|
+
- branch: str
|
|
284
|
+
- error: str | None
|
|
285
|
+
|
|
286
|
+
Raises:
|
|
287
|
+
RuntimeError: If git is not available or not in a repository
|
|
288
|
+
ValueError: If force=True and branch is "main"
|
|
289
|
+
"""
|
|
290
|
+
path = path or Path.cwd()
|
|
291
|
+
|
|
292
|
+
# Safety check: never force push to main
|
|
293
|
+
if force and branch == "main":
|
|
294
|
+
raise ValueError("Cannot force push to main branch")
|
|
295
|
+
|
|
296
|
+
# Plan 3.3: no_direct_push_main from policies.yaml
|
|
297
|
+
if branch == "main":
|
|
298
|
+
try:
|
|
299
|
+
from .policy_loader import load_policies
|
|
300
|
+
p = load_policies(path)
|
|
301
|
+
if p and p.branch_protection and getattr(p.branch_protection, "no_direct_push_main", True):
|
|
302
|
+
raise ValueError(
|
|
303
|
+
"Direct push to main is disallowed by .tapps-agents/policies.yaml (branch_protection.no_direct_push_main). "
|
|
304
|
+
"Create a branch and merge via PR, or adjust policies."
|
|
305
|
+
)
|
|
306
|
+
except ValueError:
|
|
307
|
+
raise
|
|
308
|
+
except Exception: # pylint: disable=broad-except
|
|
309
|
+
pass # if policy load fails, allow push
|
|
310
|
+
|
|
311
|
+
# Validate git availability
|
|
312
|
+
if not check_git_available():
|
|
313
|
+
raise RuntimeError("Git is not installed or not in PATH")
|
|
314
|
+
|
|
315
|
+
# Validate git repository
|
|
316
|
+
if not is_git_repository(path):
|
|
317
|
+
raise RuntimeError(f"Not a git repository: {path}")
|
|
318
|
+
|
|
319
|
+
try:
|
|
320
|
+
# Build push command
|
|
321
|
+
push_args = ["push"]
|
|
322
|
+
if force:
|
|
323
|
+
push_args.append("--force")
|
|
324
|
+
push_args.extend(["origin", branch])
|
|
325
|
+
|
|
326
|
+
push_result = _run_git_command(push_args, cwd=path, check=True)
|
|
327
|
+
|
|
328
|
+
logger.info(f"Pushed changes to remote branch '{branch}'")
|
|
329
|
+
|
|
330
|
+
return {
|
|
331
|
+
"success": True,
|
|
332
|
+
"branch": branch,
|
|
333
|
+
"error": None,
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
except subprocess.CalledProcessError as e:
|
|
337
|
+
error_msg = e.stderr or str(e) if e.stderr else str(e)
|
|
338
|
+
logger.error(f"Failed to push changes: {error_msg}")
|
|
339
|
+
return {
|
|
340
|
+
"success": False,
|
|
341
|
+
"branch": branch,
|
|
342
|
+
"error": error_msg,
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def create_pull_request(
|
|
347
|
+
title: str,
|
|
348
|
+
body: str,
|
|
349
|
+
head_branch: str,
|
|
350
|
+
base_branch: str = "main",
|
|
351
|
+
path: Path | None = None,
|
|
352
|
+
) -> dict[str, Any]:
|
|
353
|
+
"""
|
|
354
|
+
Create a pull request using GitHub CLI (gh) if available.
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
title: PR title
|
|
358
|
+
body: PR body/description
|
|
359
|
+
head_branch: Source branch (branch with changes)
|
|
360
|
+
base_branch: Target branch (default: "main")
|
|
361
|
+
path: Path to git repository (default: current directory)
|
|
362
|
+
|
|
363
|
+
Returns:
|
|
364
|
+
Dictionary with:
|
|
365
|
+
- success: bool
|
|
366
|
+
- pr_url: str | None
|
|
367
|
+
- pr_number: int | None
|
|
368
|
+
- error: str | None
|
|
369
|
+
"""
|
|
370
|
+
path = path or Path.cwd()
|
|
371
|
+
|
|
372
|
+
# Check if GitHub CLI is available
|
|
373
|
+
gh_path = shutil.which("gh")
|
|
374
|
+
if not gh_path:
|
|
375
|
+
logger.warning(
|
|
376
|
+
"GitHub CLI (gh) not found. PR creation requires 'gh' to be installed and authenticated."
|
|
377
|
+
)
|
|
378
|
+
return {
|
|
379
|
+
"success": False,
|
|
380
|
+
"pr_url": None,
|
|
381
|
+
"pr_number": None,
|
|
382
|
+
"error": "GitHub CLI (gh) not found. Install from https://cli.github.com/",
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
try:
|
|
386
|
+
# Create PR using GitHub CLI
|
|
387
|
+
pr_args = [
|
|
388
|
+
"pr",
|
|
389
|
+
"create",
|
|
390
|
+
"--title", title,
|
|
391
|
+
"--body", body,
|
|
392
|
+
"--head", head_branch,
|
|
393
|
+
"--base", base_branch,
|
|
394
|
+
]
|
|
395
|
+
|
|
396
|
+
result = subprocess.run(
|
|
397
|
+
[gh_path] + pr_args,
|
|
398
|
+
cwd=path,
|
|
399
|
+
capture_output=True,
|
|
400
|
+
text=True,
|
|
401
|
+
encoding="utf-8",
|
|
402
|
+
errors="replace",
|
|
403
|
+
check=True,
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
# Parse PR URL from output (gh outputs the PR URL)
|
|
407
|
+
output = result.stdout.strip()
|
|
408
|
+
pr_url = output if output.startswith("http") else None
|
|
409
|
+
|
|
410
|
+
# Try to extract PR number from URL
|
|
411
|
+
pr_number = None
|
|
412
|
+
if pr_url:
|
|
413
|
+
# URL format: https://github.com/owner/repo/pull/123
|
|
414
|
+
parts = pr_url.split("/")
|
|
415
|
+
if "pull" in parts:
|
|
416
|
+
try:
|
|
417
|
+
pr_number = int(parts[parts.index("pull") + 1])
|
|
418
|
+
except (ValueError, IndexError):
|
|
419
|
+
pass
|
|
420
|
+
|
|
421
|
+
logger.info(f"Created pull request: {pr_url or output}")
|
|
422
|
+
|
|
423
|
+
return {
|
|
424
|
+
"success": True,
|
|
425
|
+
"pr_url": pr_url,
|
|
426
|
+
"pr_number": pr_number,
|
|
427
|
+
"error": None,
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
except subprocess.CalledProcessError as e:
|
|
431
|
+
error_msg = e.stderr or str(e) if e.stderr else str(e)
|
|
432
|
+
logger.error(f"Failed to create pull request: {error_msg}")
|
|
433
|
+
return {
|
|
434
|
+
"success": False,
|
|
435
|
+
"pr_url": None,
|
|
436
|
+
"pr_number": None,
|
|
437
|
+
"error": error_msg,
|
|
438
|
+
}
|
|
439
|
+
except FileNotFoundError:
|
|
440
|
+
return {
|
|
441
|
+
"success": False,
|
|
442
|
+
"pr_url": None,
|
|
443
|
+
"pr_number": None,
|
|
444
|
+
"error": "GitHub CLI (gh) not found",
|
|
445
|
+
}
|