tapps-agents 3.5.40__py3-none-any.whl → 3.6.0__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/scoring.py +1566 -1566
- tapps_agents/agents/reviewer/service_discovery.py +534 -534
- tapps_agents/agents/reviewer/tools/__init__.py +41 -41
- 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/health.py +665 -665
- 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 -219
- tapps_agents/cli/commands/tester.py +191 -191
- tapps_agents/cli/commands/top_level.py +3586 -3586
- 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/artifact_context_builder.py +293 -0
- 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/config.py +33 -0
- 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.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/orchestrator.py +271 -271
- 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/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/base.py +185 -185
- 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/build_orchestrator.py +2700 -2667
- 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/fix_orchestrator.py +723 -723
- 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_executor.py +2337 -2196
- 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/message_formatter.py +188 -188
- 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 -111
- 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.5.40.dist-info → tapps_agents-3.6.0.dist-info}/METADATA +672 -672
- tapps_agents-3.6.0.dist-info/RECORD +758 -0
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/licenses/LICENSE +22 -22
- tapps_agents/health/checks/outcomes.backup_20260204_064058.py +0 -324
- tapps_agents/health/checks/outcomes.backup_20260204_064256.py +0 -324
- tapps_agents/health/checks/outcomes.backup_20260204_064600.py +0 -324
- tapps_agents-3.5.40.dist-info/RECORD +0 -760
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/WHEEL +0 -0
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/entry_points.txt +0 -0
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/top_level.txt +0 -0
|
@@ -1,443 +1,443 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Reviewer Result Cache - Cache reviewer command results based on file content hash.
|
|
3
|
-
|
|
4
|
-
2025 Performance Pattern: Result caching for reviewer commands (score, review, lint).
|
|
5
|
-
Provides 90%+ speedup for unchanged files by caching results based on file content hash.
|
|
6
|
-
|
|
7
|
-
References:
|
|
8
|
-
- docs/PERFORMANCE_OPTIMIZATION_RECOMMENDATIONS_2025.md
|
|
9
|
-
- docs/PERFORMANCE_PATTERNS_QUICK_REFERENCE.md
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
from __future__ import annotations
|
|
13
|
-
|
|
14
|
-
import hashlib
|
|
15
|
-
import json
|
|
16
|
-
import logging
|
|
17
|
-
import os
|
|
18
|
-
from datetime import UTC, datetime
|
|
19
|
-
from pathlib import Path
|
|
20
|
-
from typing import Any
|
|
21
|
-
|
|
22
|
-
logger = logging.getLogger(__name__)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class ReviewerResultCache:
|
|
26
|
-
"""
|
|
27
|
-
Cache reviewer command results based on file content hash.
|
|
28
|
-
|
|
29
|
-
Features:
|
|
30
|
-
- Content-based cache keys (invalidates on file changes)
|
|
31
|
-
- Version-aware caching (invalidates on command version changes)
|
|
32
|
-
- TTL-based expiration (configurable)
|
|
33
|
-
- Atomic file operations (thread-safe)
|
|
34
|
-
|
|
35
|
-
Cache Key Format: {file_path}:{content_hash}:{command}:{version}
|
|
36
|
-
|
|
37
|
-
Example:
|
|
38
|
-
cache = ReviewerResultCache()
|
|
39
|
-
|
|
40
|
-
# Check cache before execution
|
|
41
|
-
cached = await cache.get_cached_result(file_path, "score", version="1.0")
|
|
42
|
-
if cached:
|
|
43
|
-
return cached
|
|
44
|
-
|
|
45
|
-
# Execute and cache
|
|
46
|
-
result = await reviewer.run("score", file=str(file_path))
|
|
47
|
-
await cache.save_result(file_path, "score", "1.0", result)
|
|
48
|
-
"""
|
|
49
|
-
|
|
50
|
-
# Default cache version (increment to invalidate all caches)
|
|
51
|
-
CACHE_VERSION = "1.0.0"
|
|
52
|
-
|
|
53
|
-
# Default TTL in seconds (1 hour)
|
|
54
|
-
DEFAULT_TTL = 3600
|
|
55
|
-
|
|
56
|
-
def __init__(
|
|
57
|
-
self,
|
|
58
|
-
cache_dir: Path | None = None,
|
|
59
|
-
ttl_seconds: int = DEFAULT_TTL,
|
|
60
|
-
enabled: bool = True,
|
|
61
|
-
):
|
|
62
|
-
"""
|
|
63
|
-
Initialize the reviewer result cache.
|
|
64
|
-
|
|
65
|
-
Args:
|
|
66
|
-
cache_dir: Directory for cache files (default: .tapps-agents/cache/reviewer)
|
|
67
|
-
ttl_seconds: Time-to-live for cache entries in seconds (default: 3600)
|
|
68
|
-
enabled: Whether caching is enabled (default: True)
|
|
69
|
-
"""
|
|
70
|
-
if cache_dir is None:
|
|
71
|
-
# Use project root detection instead of current working directory
|
|
72
|
-
from ...core.path_validator import PathValidator
|
|
73
|
-
validator = PathValidator()
|
|
74
|
-
cache_dir = validator.project_root / ".tapps-agents" / "cache" / "reviewer"
|
|
75
|
-
|
|
76
|
-
self.cache_dir = cache_dir
|
|
77
|
-
self.ttl_seconds = ttl_seconds
|
|
78
|
-
self.enabled = enabled
|
|
79
|
-
|
|
80
|
-
# Metadata file tracks cache entries and their file hashes
|
|
81
|
-
self.metadata_file = self.cache_dir / "metadata.json"
|
|
82
|
-
self._metadata: dict[str, dict[str, Any]] | None = None
|
|
83
|
-
|
|
84
|
-
# Statistics
|
|
85
|
-
self._hits = 0
|
|
86
|
-
self._misses = 0
|
|
87
|
-
|
|
88
|
-
# Create cache directory
|
|
89
|
-
if self.enabled:
|
|
90
|
-
self.cache_dir.mkdir(parents=True, exist_ok=True)
|
|
91
|
-
|
|
92
|
-
@property
|
|
93
|
-
def metadata(self) -> dict[str, dict[str, Any]]:
|
|
94
|
-
"""Load and return cache metadata."""
|
|
95
|
-
if self._metadata is None:
|
|
96
|
-
self._metadata = self._load_metadata()
|
|
97
|
-
return self._metadata
|
|
98
|
-
|
|
99
|
-
def _load_metadata(self) -> dict[str, dict[str, Any]]:
|
|
100
|
-
"""Load cache metadata from file."""
|
|
101
|
-
if not self.metadata_file.exists():
|
|
102
|
-
return {}
|
|
103
|
-
|
|
104
|
-
try:
|
|
105
|
-
content = self.metadata_file.read_text(encoding="utf-8")
|
|
106
|
-
return json.loads(content)
|
|
107
|
-
except (json.JSONDecodeError, OSError) as e:
|
|
108
|
-
logger.warning(f"Failed to load cache metadata: {e}")
|
|
109
|
-
return {}
|
|
110
|
-
|
|
111
|
-
def _save_metadata(self) -> None:
|
|
112
|
-
"""Save cache metadata to file (atomic write)."""
|
|
113
|
-
if self._metadata is None:
|
|
114
|
-
return
|
|
115
|
-
|
|
116
|
-
try:
|
|
117
|
-
# Atomic write: write to temp file, then rename
|
|
118
|
-
temp_file = self.metadata_file.with_suffix(".tmp")
|
|
119
|
-
temp_file.write_text(
|
|
120
|
-
json.dumps(self._metadata, indent=2),
|
|
121
|
-
encoding="utf-8"
|
|
122
|
-
)
|
|
123
|
-
temp_file.replace(self.metadata_file)
|
|
124
|
-
except OSError as e:
|
|
125
|
-
logger.warning(f"Failed to save cache metadata: {e}")
|
|
126
|
-
|
|
127
|
-
def _file_hash(self, file_path: Path) -> str:
|
|
128
|
-
"""
|
|
129
|
-
Compute file content hash.
|
|
130
|
-
|
|
131
|
-
Uses SHA-256 truncated to 16 characters for reasonable uniqueness
|
|
132
|
-
while keeping cache keys manageable.
|
|
133
|
-
"""
|
|
134
|
-
try:
|
|
135
|
-
content = file_path.read_bytes()
|
|
136
|
-
return hashlib.sha256(content).hexdigest()[:16]
|
|
137
|
-
except OSError as e:
|
|
138
|
-
logger.warning(f"Failed to hash file {file_path}: {e}")
|
|
139
|
-
return ""
|
|
140
|
-
|
|
141
|
-
def _make_cache_key(
|
|
142
|
-
self,
|
|
143
|
-
file_path: Path,
|
|
144
|
-
command: str,
|
|
145
|
-
version: str,
|
|
146
|
-
) -> str:
|
|
147
|
-
"""
|
|
148
|
-
Create cache key from file path, content hash, command, and version.
|
|
149
|
-
|
|
150
|
-
Format: {normalized_path}:{content_hash}:{command}:{version}
|
|
151
|
-
"""
|
|
152
|
-
# Normalize path for consistent keys
|
|
153
|
-
normalized_path = str(file_path.resolve()).replace("\\", "/")
|
|
154
|
-
file_hash = self._file_hash(file_path)
|
|
155
|
-
|
|
156
|
-
return f"{normalized_path}:{file_hash}:{command}:{version}"
|
|
157
|
-
|
|
158
|
-
def _get_cache_file(self, cache_key: str) -> Path:
|
|
159
|
-
"""Get cache file path for a cache key."""
|
|
160
|
-
# Use hash of cache key for filename (to handle long paths)
|
|
161
|
-
key_hash = hashlib.sha256(cache_key.encode()).hexdigest()[:32]
|
|
162
|
-
return self.cache_dir / f"{key_hash}.json"
|
|
163
|
-
|
|
164
|
-
def _is_cache_valid(self, cache_key: str) -> bool:
|
|
165
|
-
"""
|
|
166
|
-
Check if cache entry is valid (not expired, file unchanged).
|
|
167
|
-
"""
|
|
168
|
-
if cache_key not in self.metadata:
|
|
169
|
-
return False
|
|
170
|
-
|
|
171
|
-
entry = self.metadata[cache_key]
|
|
172
|
-
|
|
173
|
-
# Check TTL
|
|
174
|
-
cached_at = entry.get("cached_at", "")
|
|
175
|
-
if cached_at:
|
|
176
|
-
try:
|
|
177
|
-
cached_time = datetime.fromisoformat(cached_at.replace("Z", "+00:00"))
|
|
178
|
-
age_seconds = (datetime.now(UTC) - cached_time).total_seconds()
|
|
179
|
-
if age_seconds > self.ttl_seconds:
|
|
180
|
-
logger.debug(f"Cache expired for {cache_key} (age: {age_seconds:.0f}s)")
|
|
181
|
-
return False
|
|
182
|
-
except ValueError:
|
|
183
|
-
pass
|
|
184
|
-
|
|
185
|
-
# Check file hash (content-based invalidation)
|
|
186
|
-
file_path = Path(entry.get("file_path", ""))
|
|
187
|
-
if file_path.exists():
|
|
188
|
-
current_hash = self._file_hash(file_path)
|
|
189
|
-
cached_hash = entry.get("file_hash", "")
|
|
190
|
-
if current_hash != cached_hash:
|
|
191
|
-
logger.debug(f"Cache invalidated for {cache_key} (file changed)")
|
|
192
|
-
return False
|
|
193
|
-
|
|
194
|
-
return True
|
|
195
|
-
|
|
196
|
-
async def get_cached_result(
|
|
197
|
-
self,
|
|
198
|
-
file_path: Path,
|
|
199
|
-
command: str,
|
|
200
|
-
version: str = CACHE_VERSION,
|
|
201
|
-
) -> dict[str, Any] | None:
|
|
202
|
-
"""
|
|
203
|
-
Get cached result if file unchanged and cache valid.
|
|
204
|
-
|
|
205
|
-
Args:
|
|
206
|
-
file_path: Path to the file being analyzed
|
|
207
|
-
command: Command name (e.g., "score", "review", "lint")
|
|
208
|
-
version: Command version for cache invalidation
|
|
209
|
-
|
|
210
|
-
Returns:
|
|
211
|
-
Cached result dict or None if cache miss
|
|
212
|
-
"""
|
|
213
|
-
if not self.enabled:
|
|
214
|
-
return None
|
|
215
|
-
|
|
216
|
-
if not file_path.exists():
|
|
217
|
-
return None
|
|
218
|
-
|
|
219
|
-
# Try current version first
|
|
220
|
-
cache_key = self._make_cache_key(file_path, command, version)
|
|
221
|
-
|
|
222
|
-
# Check if cache is valid
|
|
223
|
-
if self._is_cache_valid(cache_key):
|
|
224
|
-
cache_file = self._get_cache_file(cache_key)
|
|
225
|
-
if cache_file.exists():
|
|
226
|
-
try:
|
|
227
|
-
content = cache_file.read_text(encoding="utf-8")
|
|
228
|
-
result = json.loads(content)
|
|
229
|
-
self._hits += 1
|
|
230
|
-
logger.debug(f"Cache hit for {file_path} ({command})")
|
|
231
|
-
return result
|
|
232
|
-
except (json.JSONDecodeError, OSError) as e:
|
|
233
|
-
logger.warning(f"Failed to load cache for {file_path}: {e}")
|
|
234
|
-
|
|
235
|
-
# Backward compatibility: Check for cache entries with package version
|
|
236
|
-
# (old cache entries may have been saved with package version instead of cache version)
|
|
237
|
-
try:
|
|
238
|
-
from ... import __version__ as package_version
|
|
239
|
-
if package_version != version:
|
|
240
|
-
old_cache_key = self._make_cache_key(file_path, command, package_version)
|
|
241
|
-
if self._is_cache_valid(old_cache_key):
|
|
242
|
-
cache_file = self._get_cache_file(old_cache_key)
|
|
243
|
-
if cache_file.exists():
|
|
244
|
-
try:
|
|
245
|
-
content = cache_file.read_text(encoding="utf-8")
|
|
246
|
-
result = json.loads(content)
|
|
247
|
-
# Migrate to new cache key format
|
|
248
|
-
await self.save_result(file_path, command, version, result)
|
|
249
|
-
# Remove old cache entry
|
|
250
|
-
if old_cache_key in self.metadata:
|
|
251
|
-
self.metadata.pop(old_cache_key, None)
|
|
252
|
-
try:
|
|
253
|
-
cache_file.unlink(missing_ok=True)
|
|
254
|
-
except OSError:
|
|
255
|
-
pass
|
|
256
|
-
self._save_metadata()
|
|
257
|
-
self._hits += 1
|
|
258
|
-
logger.debug(f"Cache hit for {file_path} ({command}) - migrated from package version")
|
|
259
|
-
return result
|
|
260
|
-
except (json.JSONDecodeError, OSError) as e:
|
|
261
|
-
logger.warning(f"Failed to load old cache for {file_path}: {e}")
|
|
262
|
-
except ImportError:
|
|
263
|
-
# Package version not available, skip backward compatibility check
|
|
264
|
-
pass
|
|
265
|
-
|
|
266
|
-
self._misses += 1
|
|
267
|
-
return None
|
|
268
|
-
|
|
269
|
-
async def save_result(
|
|
270
|
-
self,
|
|
271
|
-
file_path: Path,
|
|
272
|
-
command: str,
|
|
273
|
-
version: str,
|
|
274
|
-
result: dict[str, Any],
|
|
275
|
-
) -> None:
|
|
276
|
-
"""
|
|
277
|
-
Save result to cache.
|
|
278
|
-
|
|
279
|
-
Args:
|
|
280
|
-
file_path: Path to the file being analyzed
|
|
281
|
-
command: Command name (e.g., "score", "review", "lint")
|
|
282
|
-
version: Command version for cache invalidation
|
|
283
|
-
result: Result to cache
|
|
284
|
-
"""
|
|
285
|
-
if not self.enabled:
|
|
286
|
-
return
|
|
287
|
-
|
|
288
|
-
if not file_path.exists():
|
|
289
|
-
return
|
|
290
|
-
|
|
291
|
-
cache_key = self._make_cache_key(file_path, command, version)
|
|
292
|
-
file_hash = self._file_hash(file_path)
|
|
293
|
-
|
|
294
|
-
# Save result to cache file
|
|
295
|
-
cache_file = self._get_cache_file(cache_key)
|
|
296
|
-
try:
|
|
297
|
-
cache_file.write_text(
|
|
298
|
-
json.dumps(result, indent=2, default=str),
|
|
299
|
-
encoding="utf-8"
|
|
300
|
-
)
|
|
301
|
-
except OSError as e:
|
|
302
|
-
logger.warning(f"Failed to save cache for {file_path}: {e}")
|
|
303
|
-
return
|
|
304
|
-
|
|
305
|
-
# Update metadata
|
|
306
|
-
self.metadata[cache_key] = {
|
|
307
|
-
"file_path": str(file_path.resolve()),
|
|
308
|
-
"file_hash": file_hash,
|
|
309
|
-
"command": command,
|
|
310
|
-
"version": version,
|
|
311
|
-
"cached_at": datetime.now(UTC).isoformat() + "Z",
|
|
312
|
-
}
|
|
313
|
-
self._save_metadata()
|
|
314
|
-
|
|
315
|
-
logger.debug(f"Cached result for {file_path} ({command})")
|
|
316
|
-
|
|
317
|
-
def invalidate_file(self, file_path: Path) -> int:
|
|
318
|
-
"""
|
|
319
|
-
Invalidate all cache entries for a file.
|
|
320
|
-
|
|
321
|
-
Returns:
|
|
322
|
-
Number of entries invalidated
|
|
323
|
-
"""
|
|
324
|
-
if not self.enabled:
|
|
325
|
-
return 0
|
|
326
|
-
|
|
327
|
-
normalized_path = str(file_path.resolve()).replace("\\", "/")
|
|
328
|
-
keys_to_remove = [
|
|
329
|
-
key for key in self.metadata
|
|
330
|
-
if key.startswith(normalized_path + ":")
|
|
331
|
-
]
|
|
332
|
-
|
|
333
|
-
for key in keys_to_remove:
|
|
334
|
-
self.metadata.pop(key, None)
|
|
335
|
-
cache_file = self._get_cache_file(key)
|
|
336
|
-
try:
|
|
337
|
-
cache_file.unlink(missing_ok=True)
|
|
338
|
-
except OSError:
|
|
339
|
-
pass
|
|
340
|
-
|
|
341
|
-
if keys_to_remove:
|
|
342
|
-
self._save_metadata()
|
|
343
|
-
|
|
344
|
-
return len(keys_to_remove)
|
|
345
|
-
|
|
346
|
-
def clear(self) -> int:
|
|
347
|
-
"""
|
|
348
|
-
Clear all cache entries.
|
|
349
|
-
|
|
350
|
-
Returns:
|
|
351
|
-
Number of entries cleared
|
|
352
|
-
"""
|
|
353
|
-
if not self.enabled:
|
|
354
|
-
return 0
|
|
355
|
-
|
|
356
|
-
count = len(self.metadata)
|
|
357
|
-
|
|
358
|
-
# Remove cache files
|
|
359
|
-
for key in self.metadata:
|
|
360
|
-
cache_file = self._get_cache_file(key)
|
|
361
|
-
try:
|
|
362
|
-
cache_file.unlink(missing_ok=True)
|
|
363
|
-
except OSError:
|
|
364
|
-
pass
|
|
365
|
-
|
|
366
|
-
# Clear metadata
|
|
367
|
-
self._metadata = {}
|
|
368
|
-
self._save_metadata()
|
|
369
|
-
|
|
370
|
-
return count
|
|
371
|
-
|
|
372
|
-
def get_stats(self) -> dict[str, Any]:
|
|
373
|
-
"""
|
|
374
|
-
Get cache statistics.
|
|
375
|
-
|
|
376
|
-
Returns:
|
|
377
|
-
Dictionary with cache statistics
|
|
378
|
-
"""
|
|
379
|
-
total_requests = self._hits + self._misses
|
|
380
|
-
hit_rate = (self._hits / total_requests * 100) if total_requests > 0 else 0.0
|
|
381
|
-
|
|
382
|
-
return {
|
|
383
|
-
"enabled": self.enabled,
|
|
384
|
-
"cache_dir": str(self.cache_dir),
|
|
385
|
-
"ttl_seconds": self.ttl_seconds,
|
|
386
|
-
"entries": len(self.metadata),
|
|
387
|
-
"hits": self._hits,
|
|
388
|
-
"misses": self._misses,
|
|
389
|
-
"hit_rate": f"{hit_rate:.1f}%",
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
def prune_expired(self) -> int:
|
|
393
|
-
"""
|
|
394
|
-
Remove expired cache entries.
|
|
395
|
-
|
|
396
|
-
Returns:
|
|
397
|
-
Number of entries pruned
|
|
398
|
-
"""
|
|
399
|
-
if not self.enabled:
|
|
400
|
-
return 0
|
|
401
|
-
|
|
402
|
-
keys_to_remove = [
|
|
403
|
-
key for key in self.metadata
|
|
404
|
-
if not self._is_cache_valid(key)
|
|
405
|
-
]
|
|
406
|
-
|
|
407
|
-
for key in keys_to_remove:
|
|
408
|
-
self.metadata.pop(key, None)
|
|
409
|
-
cache_file = self._get_cache_file(key)
|
|
410
|
-
try:
|
|
411
|
-
cache_file.unlink(missing_ok=True)
|
|
412
|
-
except OSError:
|
|
413
|
-
pass
|
|
414
|
-
|
|
415
|
-
if keys_to_remove:
|
|
416
|
-
self._save_metadata()
|
|
417
|
-
|
|
418
|
-
return len(keys_to_remove)
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
# Global cache instance (lazy initialization)
|
|
422
|
-
_global_cache: ReviewerResultCache | None = None
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
def get_reviewer_cache() -> ReviewerResultCache:
|
|
426
|
-
"""
|
|
427
|
-
Get the global reviewer cache instance.
|
|
428
|
-
|
|
429
|
-
Returns:
|
|
430
|
-
Global ReviewerResultCache instance
|
|
431
|
-
"""
|
|
432
|
-
global _global_cache
|
|
433
|
-
if _global_cache is None:
|
|
434
|
-
# Check if caching is enabled via environment variable
|
|
435
|
-
enabled = os.getenv("TAPPS_AGENTS_CACHE_ENABLED", "true").lower() == "true"
|
|
436
|
-
_global_cache = ReviewerResultCache(enabled=enabled)
|
|
437
|
-
return _global_cache
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
def reset_reviewer_cache() -> None:
|
|
441
|
-
"""Reset the global reviewer cache instance."""
|
|
442
|
-
global _global_cache
|
|
443
|
-
_global_cache = None
|
|
1
|
+
"""
|
|
2
|
+
Reviewer Result Cache - Cache reviewer command results based on file content hash.
|
|
3
|
+
|
|
4
|
+
2025 Performance Pattern: Result caching for reviewer commands (score, review, lint).
|
|
5
|
+
Provides 90%+ speedup for unchanged files by caching results based on file content hash.
|
|
6
|
+
|
|
7
|
+
References:
|
|
8
|
+
- docs/PERFORMANCE_OPTIMIZATION_RECOMMENDATIONS_2025.md
|
|
9
|
+
- docs/PERFORMANCE_PATTERNS_QUICK_REFERENCE.md
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import hashlib
|
|
15
|
+
import json
|
|
16
|
+
import logging
|
|
17
|
+
import os
|
|
18
|
+
from datetime import UTC, datetime
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ReviewerResultCache:
|
|
26
|
+
"""
|
|
27
|
+
Cache reviewer command results based on file content hash.
|
|
28
|
+
|
|
29
|
+
Features:
|
|
30
|
+
- Content-based cache keys (invalidates on file changes)
|
|
31
|
+
- Version-aware caching (invalidates on command version changes)
|
|
32
|
+
- TTL-based expiration (configurable)
|
|
33
|
+
- Atomic file operations (thread-safe)
|
|
34
|
+
|
|
35
|
+
Cache Key Format: {file_path}:{content_hash}:{command}:{version}
|
|
36
|
+
|
|
37
|
+
Example:
|
|
38
|
+
cache = ReviewerResultCache()
|
|
39
|
+
|
|
40
|
+
# Check cache before execution
|
|
41
|
+
cached = await cache.get_cached_result(file_path, "score", version="1.0")
|
|
42
|
+
if cached:
|
|
43
|
+
return cached
|
|
44
|
+
|
|
45
|
+
# Execute and cache
|
|
46
|
+
result = await reviewer.run("score", file=str(file_path))
|
|
47
|
+
await cache.save_result(file_path, "score", "1.0", result)
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
# Default cache version (increment to invalidate all caches)
|
|
51
|
+
CACHE_VERSION = "1.0.0"
|
|
52
|
+
|
|
53
|
+
# Default TTL in seconds (1 hour)
|
|
54
|
+
DEFAULT_TTL = 3600
|
|
55
|
+
|
|
56
|
+
def __init__(
|
|
57
|
+
self,
|
|
58
|
+
cache_dir: Path | None = None,
|
|
59
|
+
ttl_seconds: int = DEFAULT_TTL,
|
|
60
|
+
enabled: bool = True,
|
|
61
|
+
):
|
|
62
|
+
"""
|
|
63
|
+
Initialize the reviewer result cache.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
cache_dir: Directory for cache files (default: .tapps-agents/cache/reviewer)
|
|
67
|
+
ttl_seconds: Time-to-live for cache entries in seconds (default: 3600)
|
|
68
|
+
enabled: Whether caching is enabled (default: True)
|
|
69
|
+
"""
|
|
70
|
+
if cache_dir is None:
|
|
71
|
+
# Use project root detection instead of current working directory
|
|
72
|
+
from ...core.path_validator import PathValidator
|
|
73
|
+
validator = PathValidator()
|
|
74
|
+
cache_dir = validator.project_root / ".tapps-agents" / "cache" / "reviewer"
|
|
75
|
+
|
|
76
|
+
self.cache_dir = cache_dir
|
|
77
|
+
self.ttl_seconds = ttl_seconds
|
|
78
|
+
self.enabled = enabled
|
|
79
|
+
|
|
80
|
+
# Metadata file tracks cache entries and their file hashes
|
|
81
|
+
self.metadata_file = self.cache_dir / "metadata.json"
|
|
82
|
+
self._metadata: dict[str, dict[str, Any]] | None = None
|
|
83
|
+
|
|
84
|
+
# Statistics
|
|
85
|
+
self._hits = 0
|
|
86
|
+
self._misses = 0
|
|
87
|
+
|
|
88
|
+
# Create cache directory
|
|
89
|
+
if self.enabled:
|
|
90
|
+
self.cache_dir.mkdir(parents=True, exist_ok=True)
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def metadata(self) -> dict[str, dict[str, Any]]:
|
|
94
|
+
"""Load and return cache metadata."""
|
|
95
|
+
if self._metadata is None:
|
|
96
|
+
self._metadata = self._load_metadata()
|
|
97
|
+
return self._metadata
|
|
98
|
+
|
|
99
|
+
def _load_metadata(self) -> dict[str, dict[str, Any]]:
|
|
100
|
+
"""Load cache metadata from file."""
|
|
101
|
+
if not self.metadata_file.exists():
|
|
102
|
+
return {}
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
content = self.metadata_file.read_text(encoding="utf-8")
|
|
106
|
+
return json.loads(content)
|
|
107
|
+
except (json.JSONDecodeError, OSError) as e:
|
|
108
|
+
logger.warning(f"Failed to load cache metadata: {e}")
|
|
109
|
+
return {}
|
|
110
|
+
|
|
111
|
+
def _save_metadata(self) -> None:
|
|
112
|
+
"""Save cache metadata to file (atomic write)."""
|
|
113
|
+
if self._metadata is None:
|
|
114
|
+
return
|
|
115
|
+
|
|
116
|
+
try:
|
|
117
|
+
# Atomic write: write to temp file, then rename
|
|
118
|
+
temp_file = self.metadata_file.with_suffix(".tmp")
|
|
119
|
+
temp_file.write_text(
|
|
120
|
+
json.dumps(self._metadata, indent=2),
|
|
121
|
+
encoding="utf-8"
|
|
122
|
+
)
|
|
123
|
+
temp_file.replace(self.metadata_file)
|
|
124
|
+
except OSError as e:
|
|
125
|
+
logger.warning(f"Failed to save cache metadata: {e}")
|
|
126
|
+
|
|
127
|
+
def _file_hash(self, file_path: Path) -> str:
|
|
128
|
+
"""
|
|
129
|
+
Compute file content hash.
|
|
130
|
+
|
|
131
|
+
Uses SHA-256 truncated to 16 characters for reasonable uniqueness
|
|
132
|
+
while keeping cache keys manageable.
|
|
133
|
+
"""
|
|
134
|
+
try:
|
|
135
|
+
content = file_path.read_bytes()
|
|
136
|
+
return hashlib.sha256(content).hexdigest()[:16]
|
|
137
|
+
except OSError as e:
|
|
138
|
+
logger.warning(f"Failed to hash file {file_path}: {e}")
|
|
139
|
+
return ""
|
|
140
|
+
|
|
141
|
+
def _make_cache_key(
|
|
142
|
+
self,
|
|
143
|
+
file_path: Path,
|
|
144
|
+
command: str,
|
|
145
|
+
version: str,
|
|
146
|
+
) -> str:
|
|
147
|
+
"""
|
|
148
|
+
Create cache key from file path, content hash, command, and version.
|
|
149
|
+
|
|
150
|
+
Format: {normalized_path}:{content_hash}:{command}:{version}
|
|
151
|
+
"""
|
|
152
|
+
# Normalize path for consistent keys
|
|
153
|
+
normalized_path = str(file_path.resolve()).replace("\\", "/")
|
|
154
|
+
file_hash = self._file_hash(file_path)
|
|
155
|
+
|
|
156
|
+
return f"{normalized_path}:{file_hash}:{command}:{version}"
|
|
157
|
+
|
|
158
|
+
def _get_cache_file(self, cache_key: str) -> Path:
|
|
159
|
+
"""Get cache file path for a cache key."""
|
|
160
|
+
# Use hash of cache key for filename (to handle long paths)
|
|
161
|
+
key_hash = hashlib.sha256(cache_key.encode()).hexdigest()[:32]
|
|
162
|
+
return self.cache_dir / f"{key_hash}.json"
|
|
163
|
+
|
|
164
|
+
def _is_cache_valid(self, cache_key: str) -> bool:
|
|
165
|
+
"""
|
|
166
|
+
Check if cache entry is valid (not expired, file unchanged).
|
|
167
|
+
"""
|
|
168
|
+
if cache_key not in self.metadata:
|
|
169
|
+
return False
|
|
170
|
+
|
|
171
|
+
entry = self.metadata[cache_key]
|
|
172
|
+
|
|
173
|
+
# Check TTL
|
|
174
|
+
cached_at = entry.get("cached_at", "")
|
|
175
|
+
if cached_at:
|
|
176
|
+
try:
|
|
177
|
+
cached_time = datetime.fromisoformat(cached_at.replace("Z", "+00:00"))
|
|
178
|
+
age_seconds = (datetime.now(UTC) - cached_time).total_seconds()
|
|
179
|
+
if age_seconds > self.ttl_seconds:
|
|
180
|
+
logger.debug(f"Cache expired for {cache_key} (age: {age_seconds:.0f}s)")
|
|
181
|
+
return False
|
|
182
|
+
except ValueError:
|
|
183
|
+
pass
|
|
184
|
+
|
|
185
|
+
# Check file hash (content-based invalidation)
|
|
186
|
+
file_path = Path(entry.get("file_path", ""))
|
|
187
|
+
if file_path.exists():
|
|
188
|
+
current_hash = self._file_hash(file_path)
|
|
189
|
+
cached_hash = entry.get("file_hash", "")
|
|
190
|
+
if current_hash != cached_hash:
|
|
191
|
+
logger.debug(f"Cache invalidated for {cache_key} (file changed)")
|
|
192
|
+
return False
|
|
193
|
+
|
|
194
|
+
return True
|
|
195
|
+
|
|
196
|
+
async def get_cached_result(
|
|
197
|
+
self,
|
|
198
|
+
file_path: Path,
|
|
199
|
+
command: str,
|
|
200
|
+
version: str = CACHE_VERSION,
|
|
201
|
+
) -> dict[str, Any] | None:
|
|
202
|
+
"""
|
|
203
|
+
Get cached result if file unchanged and cache valid.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
file_path: Path to the file being analyzed
|
|
207
|
+
command: Command name (e.g., "score", "review", "lint")
|
|
208
|
+
version: Command version for cache invalidation
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
Cached result dict or None if cache miss
|
|
212
|
+
"""
|
|
213
|
+
if not self.enabled:
|
|
214
|
+
return None
|
|
215
|
+
|
|
216
|
+
if not file_path.exists():
|
|
217
|
+
return None
|
|
218
|
+
|
|
219
|
+
# Try current version first
|
|
220
|
+
cache_key = self._make_cache_key(file_path, command, version)
|
|
221
|
+
|
|
222
|
+
# Check if cache is valid
|
|
223
|
+
if self._is_cache_valid(cache_key):
|
|
224
|
+
cache_file = self._get_cache_file(cache_key)
|
|
225
|
+
if cache_file.exists():
|
|
226
|
+
try:
|
|
227
|
+
content = cache_file.read_text(encoding="utf-8")
|
|
228
|
+
result = json.loads(content)
|
|
229
|
+
self._hits += 1
|
|
230
|
+
logger.debug(f"Cache hit for {file_path} ({command})")
|
|
231
|
+
return result
|
|
232
|
+
except (json.JSONDecodeError, OSError) as e:
|
|
233
|
+
logger.warning(f"Failed to load cache for {file_path}: {e}")
|
|
234
|
+
|
|
235
|
+
# Backward compatibility: Check for cache entries with package version
|
|
236
|
+
# (old cache entries may have been saved with package version instead of cache version)
|
|
237
|
+
try:
|
|
238
|
+
from ... import __version__ as package_version
|
|
239
|
+
if package_version != version:
|
|
240
|
+
old_cache_key = self._make_cache_key(file_path, command, package_version)
|
|
241
|
+
if self._is_cache_valid(old_cache_key):
|
|
242
|
+
cache_file = self._get_cache_file(old_cache_key)
|
|
243
|
+
if cache_file.exists():
|
|
244
|
+
try:
|
|
245
|
+
content = cache_file.read_text(encoding="utf-8")
|
|
246
|
+
result = json.loads(content)
|
|
247
|
+
# Migrate to new cache key format
|
|
248
|
+
await self.save_result(file_path, command, version, result)
|
|
249
|
+
# Remove old cache entry
|
|
250
|
+
if old_cache_key in self.metadata:
|
|
251
|
+
self.metadata.pop(old_cache_key, None)
|
|
252
|
+
try:
|
|
253
|
+
cache_file.unlink(missing_ok=True)
|
|
254
|
+
except OSError:
|
|
255
|
+
pass
|
|
256
|
+
self._save_metadata()
|
|
257
|
+
self._hits += 1
|
|
258
|
+
logger.debug(f"Cache hit for {file_path} ({command}) - migrated from package version")
|
|
259
|
+
return result
|
|
260
|
+
except (json.JSONDecodeError, OSError) as e:
|
|
261
|
+
logger.warning(f"Failed to load old cache for {file_path}: {e}")
|
|
262
|
+
except ImportError:
|
|
263
|
+
# Package version not available, skip backward compatibility check
|
|
264
|
+
pass
|
|
265
|
+
|
|
266
|
+
self._misses += 1
|
|
267
|
+
return None
|
|
268
|
+
|
|
269
|
+
async def save_result(
|
|
270
|
+
self,
|
|
271
|
+
file_path: Path,
|
|
272
|
+
command: str,
|
|
273
|
+
version: str,
|
|
274
|
+
result: dict[str, Any],
|
|
275
|
+
) -> None:
|
|
276
|
+
"""
|
|
277
|
+
Save result to cache.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
file_path: Path to the file being analyzed
|
|
281
|
+
command: Command name (e.g., "score", "review", "lint")
|
|
282
|
+
version: Command version for cache invalidation
|
|
283
|
+
result: Result to cache
|
|
284
|
+
"""
|
|
285
|
+
if not self.enabled:
|
|
286
|
+
return
|
|
287
|
+
|
|
288
|
+
if not file_path.exists():
|
|
289
|
+
return
|
|
290
|
+
|
|
291
|
+
cache_key = self._make_cache_key(file_path, command, version)
|
|
292
|
+
file_hash = self._file_hash(file_path)
|
|
293
|
+
|
|
294
|
+
# Save result to cache file
|
|
295
|
+
cache_file = self._get_cache_file(cache_key)
|
|
296
|
+
try:
|
|
297
|
+
cache_file.write_text(
|
|
298
|
+
json.dumps(result, indent=2, default=str),
|
|
299
|
+
encoding="utf-8"
|
|
300
|
+
)
|
|
301
|
+
except OSError as e:
|
|
302
|
+
logger.warning(f"Failed to save cache for {file_path}: {e}")
|
|
303
|
+
return
|
|
304
|
+
|
|
305
|
+
# Update metadata
|
|
306
|
+
self.metadata[cache_key] = {
|
|
307
|
+
"file_path": str(file_path.resolve()),
|
|
308
|
+
"file_hash": file_hash,
|
|
309
|
+
"command": command,
|
|
310
|
+
"version": version,
|
|
311
|
+
"cached_at": datetime.now(UTC).isoformat() + "Z",
|
|
312
|
+
}
|
|
313
|
+
self._save_metadata()
|
|
314
|
+
|
|
315
|
+
logger.debug(f"Cached result for {file_path} ({command})")
|
|
316
|
+
|
|
317
|
+
def invalidate_file(self, file_path: Path) -> int:
|
|
318
|
+
"""
|
|
319
|
+
Invalidate all cache entries for a file.
|
|
320
|
+
|
|
321
|
+
Returns:
|
|
322
|
+
Number of entries invalidated
|
|
323
|
+
"""
|
|
324
|
+
if not self.enabled:
|
|
325
|
+
return 0
|
|
326
|
+
|
|
327
|
+
normalized_path = str(file_path.resolve()).replace("\\", "/")
|
|
328
|
+
keys_to_remove = [
|
|
329
|
+
key for key in self.metadata
|
|
330
|
+
if key.startswith(normalized_path + ":")
|
|
331
|
+
]
|
|
332
|
+
|
|
333
|
+
for key in keys_to_remove:
|
|
334
|
+
self.metadata.pop(key, None)
|
|
335
|
+
cache_file = self._get_cache_file(key)
|
|
336
|
+
try:
|
|
337
|
+
cache_file.unlink(missing_ok=True)
|
|
338
|
+
except OSError:
|
|
339
|
+
pass
|
|
340
|
+
|
|
341
|
+
if keys_to_remove:
|
|
342
|
+
self._save_metadata()
|
|
343
|
+
|
|
344
|
+
return len(keys_to_remove)
|
|
345
|
+
|
|
346
|
+
def clear(self) -> int:
|
|
347
|
+
"""
|
|
348
|
+
Clear all cache entries.
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
Number of entries cleared
|
|
352
|
+
"""
|
|
353
|
+
if not self.enabled:
|
|
354
|
+
return 0
|
|
355
|
+
|
|
356
|
+
count = len(self.metadata)
|
|
357
|
+
|
|
358
|
+
# Remove cache files
|
|
359
|
+
for key in self.metadata:
|
|
360
|
+
cache_file = self._get_cache_file(key)
|
|
361
|
+
try:
|
|
362
|
+
cache_file.unlink(missing_ok=True)
|
|
363
|
+
except OSError:
|
|
364
|
+
pass
|
|
365
|
+
|
|
366
|
+
# Clear metadata
|
|
367
|
+
self._metadata = {}
|
|
368
|
+
self._save_metadata()
|
|
369
|
+
|
|
370
|
+
return count
|
|
371
|
+
|
|
372
|
+
def get_stats(self) -> dict[str, Any]:
|
|
373
|
+
"""
|
|
374
|
+
Get cache statistics.
|
|
375
|
+
|
|
376
|
+
Returns:
|
|
377
|
+
Dictionary with cache statistics
|
|
378
|
+
"""
|
|
379
|
+
total_requests = self._hits + self._misses
|
|
380
|
+
hit_rate = (self._hits / total_requests * 100) if total_requests > 0 else 0.0
|
|
381
|
+
|
|
382
|
+
return {
|
|
383
|
+
"enabled": self.enabled,
|
|
384
|
+
"cache_dir": str(self.cache_dir),
|
|
385
|
+
"ttl_seconds": self.ttl_seconds,
|
|
386
|
+
"entries": len(self.metadata),
|
|
387
|
+
"hits": self._hits,
|
|
388
|
+
"misses": self._misses,
|
|
389
|
+
"hit_rate": f"{hit_rate:.1f}%",
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
def prune_expired(self) -> int:
|
|
393
|
+
"""
|
|
394
|
+
Remove expired cache entries.
|
|
395
|
+
|
|
396
|
+
Returns:
|
|
397
|
+
Number of entries pruned
|
|
398
|
+
"""
|
|
399
|
+
if not self.enabled:
|
|
400
|
+
return 0
|
|
401
|
+
|
|
402
|
+
keys_to_remove = [
|
|
403
|
+
key for key in self.metadata
|
|
404
|
+
if not self._is_cache_valid(key)
|
|
405
|
+
]
|
|
406
|
+
|
|
407
|
+
for key in keys_to_remove:
|
|
408
|
+
self.metadata.pop(key, None)
|
|
409
|
+
cache_file = self._get_cache_file(key)
|
|
410
|
+
try:
|
|
411
|
+
cache_file.unlink(missing_ok=True)
|
|
412
|
+
except OSError:
|
|
413
|
+
pass
|
|
414
|
+
|
|
415
|
+
if keys_to_remove:
|
|
416
|
+
self._save_metadata()
|
|
417
|
+
|
|
418
|
+
return len(keys_to_remove)
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
# Global cache instance (lazy initialization)
|
|
422
|
+
_global_cache: ReviewerResultCache | None = None
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
def get_reviewer_cache() -> ReviewerResultCache:
|
|
426
|
+
"""
|
|
427
|
+
Get the global reviewer cache instance.
|
|
428
|
+
|
|
429
|
+
Returns:
|
|
430
|
+
Global ReviewerResultCache instance
|
|
431
|
+
"""
|
|
432
|
+
global _global_cache
|
|
433
|
+
if _global_cache is None:
|
|
434
|
+
# Check if caching is enabled via environment variable
|
|
435
|
+
enabled = os.getenv("TAPPS_AGENTS_CACHE_ENABLED", "true").lower() == "true"
|
|
436
|
+
_global_cache = ReviewerResultCache(enabled=enabled)
|
|
437
|
+
return _global_cache
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def reset_reviewer_cache() -> None:
|
|
441
|
+
"""Reset the global reviewer cache instance."""
|
|
442
|
+
global _global_cache
|
|
443
|
+
_global_cache = None
|