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,593 +1,593 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Context-Aware Maintainability Scorer - Language-aware maintainability analysis
|
|
3
|
-
|
|
4
|
-
Phase 3.1: Context-Aware Scoring Algorithm
|
|
5
|
-
|
|
6
|
-
Uses Strategy pattern to provide language-specific maintainability scoring
|
|
7
|
-
with pattern recognition, complexity analysis, and code structure evaluation.
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
|
-
from __future__ import annotations
|
|
11
|
-
|
|
12
|
-
import ast
|
|
13
|
-
import re
|
|
14
|
-
from abc import ABC, abstractmethod
|
|
15
|
-
from pathlib import Path
|
|
16
|
-
from typing import Any
|
|
17
|
-
|
|
18
|
-
from ...core.language_detector import Language
|
|
19
|
-
|
|
20
|
-
# Constants for maintainability scoring
|
|
21
|
-
MI_EXCELLENT_THRESHOLD = 80.0 # Maintainability Index threshold for excellent code
|
|
22
|
-
MI_POOR_THRESHOLD = 20.0 # Maintainability Index threshold for poor code
|
|
23
|
-
MI_SCALE_FACTOR = 10.0 # Scale factor to convert MI (0-100) to score (0-10)
|
|
24
|
-
MAX_NESTING_DEPTH_THRESHOLD = 3 # Nesting depth threshold before penalties
|
|
25
|
-
MAX_AVG_FUNCTION_LENGTH = 50 # Average function length threshold (lines)
|
|
26
|
-
MAX_LINE_LENGTH = 100 # Maximum line length before penalties (characters)
|
|
27
|
-
BASE_SCORE = 5.0 # Base score for neutral maintainability
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class MaintainabilityStrategy(ABC):
|
|
31
|
-
"""Base strategy for language-specific maintainability scoring."""
|
|
32
|
-
|
|
33
|
-
@abstractmethod
|
|
34
|
-
def calculate(
|
|
35
|
-
self, code: str, file_path: Path | None = None, context: dict[str, Any] | None = None
|
|
36
|
-
) -> float:
|
|
37
|
-
"""
|
|
38
|
-
Calculate maintainability score (0-10 scale, higher is better).
|
|
39
|
-
|
|
40
|
-
Args:
|
|
41
|
-
code: Source code content
|
|
42
|
-
file_path: Optional path to the file
|
|
43
|
-
context: Optional context (e.g., project structure, dependencies)
|
|
44
|
-
|
|
45
|
-
Returns:
|
|
46
|
-
Maintainability score (0-10)
|
|
47
|
-
"""
|
|
48
|
-
raise NotImplementedError
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
class PythonMaintainabilityStrategy(MaintainabilityStrategy):
|
|
52
|
-
"""Python-specific maintainability scoring strategy."""
|
|
53
|
-
|
|
54
|
-
def calculate(
|
|
55
|
-
self, code: str, file_path: Path | None = None, context: dict[str, Any] | None = None
|
|
56
|
-
) -> float:
|
|
57
|
-
"""Calculate maintainability for Python code."""
|
|
58
|
-
try:
|
|
59
|
-
# Try to use radon if available (more accurate)
|
|
60
|
-
try:
|
|
61
|
-
from radon.complexity import mi_visit
|
|
62
|
-
|
|
63
|
-
lines = code.splitlines()
|
|
64
|
-
mi_score = mi_visit(code, lines)
|
|
65
|
-
# Maintainability Index: 0-100 scale, convert to 0-10
|
|
66
|
-
# MI > 80 = good (10), MI < 20 = bad (0)
|
|
67
|
-
base_score = min(mi_score / MI_SCALE_FACTOR, 10.0)
|
|
68
|
-
except ImportError:
|
|
69
|
-
# Fallback to heuristic-based scoring
|
|
70
|
-
base_score = self._heuristic_score(code)
|
|
71
|
-
|
|
72
|
-
# Apply context-aware adjustments
|
|
73
|
-
adjustments = self._analyze_patterns(code, file_path)
|
|
74
|
-
final_score = base_score + adjustments
|
|
75
|
-
|
|
76
|
-
return max(0.0, min(10.0, final_score))
|
|
77
|
-
|
|
78
|
-
except SyntaxError:
|
|
79
|
-
return 0.0
|
|
80
|
-
except Exception:
|
|
81
|
-
return BASE_SCORE # Neutral on error
|
|
82
|
-
|
|
83
|
-
def get_issues(
|
|
84
|
-
self, code: str, file_path: Path | None = None, context: dict[str, Any] | None = None
|
|
85
|
-
) -> list[dict[str, Any]]:
|
|
86
|
-
"""
|
|
87
|
-
Get specific maintainability issues (Phase 2 - P0).
|
|
88
|
-
|
|
89
|
-
Returns list of issues with details like missing docstrings, long functions, etc.
|
|
90
|
-
"""
|
|
91
|
-
from .issue_tracking import MaintainabilityIssue
|
|
92
|
-
|
|
93
|
-
issues: list[MaintainabilityIssue] = []
|
|
94
|
-
code_lines = code.splitlines()
|
|
95
|
-
|
|
96
|
-
try:
|
|
97
|
-
tree = ast.parse(code)
|
|
98
|
-
except SyntaxError:
|
|
99
|
-
return [MaintainabilityIssue(
|
|
100
|
-
issue_type="syntax_error",
|
|
101
|
-
message="File contains syntax errors",
|
|
102
|
-
severity="high"
|
|
103
|
-
).__dict__]
|
|
104
|
-
|
|
105
|
-
# Check for missing docstrings in functions and classes
|
|
106
|
-
for node in ast.walk(tree):
|
|
107
|
-
if isinstance(node, ast.FunctionDef):
|
|
108
|
-
# Check if function has docstring
|
|
109
|
-
if not ast.get_docstring(node):
|
|
110
|
-
issues.append(MaintainabilityIssue(
|
|
111
|
-
issue_type="missing_docstring",
|
|
112
|
-
message=f"Function '{node.name}' is missing a docstring",
|
|
113
|
-
line_number=node.lineno,
|
|
114
|
-
function_name=node.name,
|
|
115
|
-
severity="medium",
|
|
116
|
-
suggestion="Add a docstring explaining the function's purpose, parameters, and return value"
|
|
117
|
-
))
|
|
118
|
-
|
|
119
|
-
# Check function length
|
|
120
|
-
if hasattr(node, "end_lineno") and node.end_lineno:
|
|
121
|
-
func_length = node.end_lineno - node.lineno
|
|
122
|
-
else:
|
|
123
|
-
# Estimate function length
|
|
124
|
-
func_length = len(code_lines[node.lineno - 1:node.lineno + 50]) if node.lineno <= len(code_lines) else 0
|
|
125
|
-
|
|
126
|
-
if func_length > MAX_AVG_FUNCTION_LENGTH:
|
|
127
|
-
severity = "high" if func_length > 100 else "medium"
|
|
128
|
-
issues.append(MaintainabilityIssue(
|
|
129
|
-
issue_type="long_function",
|
|
130
|
-
message=f"Function '{node.name}' is {func_length} lines long (threshold: {MAX_AVG_FUNCTION_LENGTH})",
|
|
131
|
-
line_number=node.lineno,
|
|
132
|
-
function_name=node.name,
|
|
133
|
-
metric_value=float(func_length),
|
|
134
|
-
threshold=float(MAX_AVG_FUNCTION_LENGTH),
|
|
135
|
-
severity=severity,
|
|
136
|
-
suggestion=f"Extract helper methods or break into smaller functions (target: <{MAX_AVG_FUNCTION_LENGTH} lines)"
|
|
137
|
-
))
|
|
138
|
-
|
|
139
|
-
# Check nesting depth
|
|
140
|
-
max_nesting = self._get_max_nesting_depth(node)
|
|
141
|
-
if max_nesting > MAX_NESTING_DEPTH_THRESHOLD:
|
|
142
|
-
severity = "high" if max_nesting > 6 else "medium"
|
|
143
|
-
issues.append(MaintainabilityIssue(
|
|
144
|
-
issue_type="deep_nesting",
|
|
145
|
-
message=f"Function '{node.name}' has {max_nesting} levels of nesting (threshold: {MAX_NESTING_DEPTH_THRESHOLD})",
|
|
146
|
-
line_number=node.lineno,
|
|
147
|
-
function_name=node.name,
|
|
148
|
-
metric_value=float(max_nesting),
|
|
149
|
-
threshold=float(MAX_NESTING_DEPTH_THRESHOLD),
|
|
150
|
-
severity=severity,
|
|
151
|
-
suggestion="Extract nested logic into separate functions to reduce complexity"
|
|
152
|
-
))
|
|
153
|
-
|
|
154
|
-
elif isinstance(node, ast.ClassDef):
|
|
155
|
-
# Check if class has docstring
|
|
156
|
-
if not ast.get_docstring(node):
|
|
157
|
-
issues.append(MaintainabilityIssue(
|
|
158
|
-
issue_type="missing_docstring",
|
|
159
|
-
message=f"Class '{node.name}' is missing a docstring",
|
|
160
|
-
line_number=node.lineno,
|
|
161
|
-
class_name=node.name,
|
|
162
|
-
severity="medium",
|
|
163
|
-
suggestion="Add a docstring explaining the class's purpose and usage"
|
|
164
|
-
))
|
|
165
|
-
|
|
166
|
-
# Check for long lines
|
|
167
|
-
for i, line in enumerate(code_lines, 1):
|
|
168
|
-
if len(line) > MAX_LINE_LENGTH:
|
|
169
|
-
issues.append(MaintainabilityIssue(
|
|
170
|
-
issue_type="long_line",
|
|
171
|
-
message=f"Line {i} is {len(line)} characters long (threshold: {MAX_LINE_LENGTH})",
|
|
172
|
-
line_number=i,
|
|
173
|
-
metric_value=float(len(line)),
|
|
174
|
-
threshold=float(MAX_LINE_LENGTH),
|
|
175
|
-
severity="low",
|
|
176
|
-
suggestion=f"Break long line into multiple lines or extract to variable"
|
|
177
|
-
))
|
|
178
|
-
|
|
179
|
-
# Check for missing type hints in function signatures
|
|
180
|
-
for node in ast.walk(tree):
|
|
181
|
-
if isinstance(node, ast.FunctionDef):
|
|
182
|
-
# Check if return type annotation is missing
|
|
183
|
-
if node.returns is None:
|
|
184
|
-
issues.append(MaintainabilityIssue(
|
|
185
|
-
issue_type="missing_type_hint",
|
|
186
|
-
message=f"Function '{node.name}' is missing return type annotation",
|
|
187
|
-
line_number=node.lineno,
|
|
188
|
-
function_name=node.name,
|
|
189
|
-
severity="low",
|
|
190
|
-
suggestion="Add return type annotation (e.g., -> str, -> int, -> None)"
|
|
191
|
-
))
|
|
192
|
-
|
|
193
|
-
# Check if all parameters have type hints
|
|
194
|
-
params_without_hints = [arg.arg for arg in node.args.args if arg.annotation is None]
|
|
195
|
-
if params_without_hints:
|
|
196
|
-
issues.append(MaintainabilityIssue(
|
|
197
|
-
issue_type="missing_type_hints",
|
|
198
|
-
message=f"Function '{node.name}' parameters missing type hints: {', '.join(params_without_hints)}",
|
|
199
|
-
line_number=node.lineno,
|
|
200
|
-
function_name=node.name,
|
|
201
|
-
severity="low",
|
|
202
|
-
suggestion="Add type annotations to all function parameters"
|
|
203
|
-
))
|
|
204
|
-
|
|
205
|
-
# Convert to dict format
|
|
206
|
-
return [
|
|
207
|
-
{
|
|
208
|
-
"issue_type": issue.issue_type,
|
|
209
|
-
"message": issue.message,
|
|
210
|
-
"line_number": issue.line_number,
|
|
211
|
-
"severity": issue.severity,
|
|
212
|
-
"suggestion": issue.suggestion,
|
|
213
|
-
"function_name": issue.function_name,
|
|
214
|
-
"class_name": issue.class_name,
|
|
215
|
-
"metric_value": issue.metric_value,
|
|
216
|
-
"threshold": issue.threshold,
|
|
217
|
-
}
|
|
218
|
-
for issue in issues
|
|
219
|
-
]
|
|
220
|
-
|
|
221
|
-
def _get_max_nesting_depth(self, node: ast.AST, current_depth: int = 0) -> int:
|
|
222
|
-
"""Calculate maximum nesting depth in an AST node."""
|
|
223
|
-
max_depth = current_depth
|
|
224
|
-
for child in ast.iter_child_nodes(node):
|
|
225
|
-
if isinstance(child, (ast.If, ast.For, ast.While, ast.Try, ast.With)):
|
|
226
|
-
child_depth = self._get_max_nesting_depth(child, current_depth + 1)
|
|
227
|
-
max_depth = max(max_depth, child_depth)
|
|
228
|
-
else:
|
|
229
|
-
child_depth = self._get_max_nesting_depth(child, current_depth)
|
|
230
|
-
max_depth = max(max_depth, child_depth)
|
|
231
|
-
return max_depth
|
|
232
|
-
|
|
233
|
-
def _heuristic_score(self, code: str) -> float:
|
|
234
|
-
"""Heuristic-based scoring when radon is not available."""
|
|
235
|
-
lines = code.split("\n")
|
|
236
|
-
total_lines = len(lines)
|
|
237
|
-
|
|
238
|
-
if total_lines == 0:
|
|
239
|
-
return BASE_SCORE
|
|
240
|
-
|
|
241
|
-
score = BASE_SCORE # Base score
|
|
242
|
-
|
|
243
|
-
# Positive factors
|
|
244
|
-
has_docstrings = bool(re.search(r'""".*?"""', code, re.DOTALL))
|
|
245
|
-
has_type_hints = bool(re.search(r"def\s+\w+\s*\([^)]*:\s*(str|int|float|bool|list|dict)", code))
|
|
246
|
-
has_async = bool(re.search(r"async\s+def|await\s+", code))
|
|
247
|
-
has_error_handling = bool(re.search(r"try\s*:|except\s+", code))
|
|
248
|
-
has_dataclasses = bool(re.search(r"@dataclass|from dataclasses import", code))
|
|
249
|
-
has_pydantic = bool(re.search(r"from pydantic import|BaseModel", code))
|
|
250
|
-
|
|
251
|
-
# Negative factors
|
|
252
|
-
long_lines = sum(1 for line in lines if len(line) > MAX_LINE_LENGTH)
|
|
253
|
-
nesting_depth = self._calculate_nesting_depth(code)
|
|
254
|
-
function_count = len(re.findall(r"def\s+\w+\s*\(", code))
|
|
255
|
-
avg_function_length = total_lines / max(function_count, 1)
|
|
256
|
-
|
|
257
|
-
# Apply adjustments
|
|
258
|
-
if has_docstrings:
|
|
259
|
-
score += 1.0
|
|
260
|
-
if has_type_hints:
|
|
261
|
-
score += 1.5
|
|
262
|
-
if has_async:
|
|
263
|
-
score += 0.5
|
|
264
|
-
if has_error_handling:
|
|
265
|
-
score += 0.5
|
|
266
|
-
if has_dataclasses or has_pydantic:
|
|
267
|
-
score += 0.5
|
|
268
|
-
|
|
269
|
-
if long_lines > 0:
|
|
270
|
-
score -= min(long_lines / total_lines * 1.5, 1.5)
|
|
271
|
-
if nesting_depth > MAX_NESTING_DEPTH_THRESHOLD:
|
|
272
|
-
score -= min((nesting_depth - MAX_NESTING_DEPTH_THRESHOLD) * 0.5, 2.0)
|
|
273
|
-
if avg_function_length > MAX_AVG_FUNCTION_LENGTH:
|
|
274
|
-
score -= min((avg_function_length - MAX_AVG_FUNCTION_LENGTH) / MAX_AVG_FUNCTION_LENGTH * 1.0, 1.5)
|
|
275
|
-
|
|
276
|
-
return max(0.0, min(10.0, score))
|
|
277
|
-
|
|
278
|
-
def _analyze_patterns(self, code: str, file_path: Path | None = None) -> float:
|
|
279
|
-
"""Analyze Python-specific patterns for maintainability adjustments."""
|
|
280
|
-
adjustments = 0.0
|
|
281
|
-
|
|
282
|
-
# Good patterns
|
|
283
|
-
if re.search(r"@lru_cache|@functools\.lru_cache", code):
|
|
284
|
-
adjustments += 0.3 # Caching improves maintainability
|
|
285
|
-
if re.search(r"from typing import|from __future__ import annotations", code):
|
|
286
|
-
adjustments += 0.2 # Type hints improve maintainability
|
|
287
|
-
if re.search(r"class\s+\w+\(.*BaseModel|class\s+\w+\(.*Pydantic", code):
|
|
288
|
-
adjustments += 0.3 # Pydantic models improve structure
|
|
289
|
-
if re.search(r"async def|asyncio\.|TaskGroup", code):
|
|
290
|
-
adjustments += 0.2 # Modern async patterns
|
|
291
|
-
|
|
292
|
-
# Bad patterns (penalties)
|
|
293
|
-
if re.search(r"eval\(|exec\(|__import__\(", code):
|
|
294
|
-
adjustments -= 1.0 # Security/maintainability risk
|
|
295
|
-
if re.search(r"global\s+\w+", code):
|
|
296
|
-
adjustments -= 0.3 # Global state reduces maintainability
|
|
297
|
-
|
|
298
|
-
return adjustments
|
|
299
|
-
|
|
300
|
-
def _calculate_nesting_depth(self, code: str) -> int:
|
|
301
|
-
"""Calculate maximum nesting depth in code."""
|
|
302
|
-
try:
|
|
303
|
-
tree = ast.parse(code)
|
|
304
|
-
max_depth = 0
|
|
305
|
-
|
|
306
|
-
def visit_node(node: ast.AST, depth: int = 0) -> None:
|
|
307
|
-
nonlocal max_depth
|
|
308
|
-
max_depth = max(max_depth, depth)
|
|
309
|
-
|
|
310
|
-
for child in ast.iter_child_nodes(node):
|
|
311
|
-
if isinstance(child, (ast.If, ast.For, ast.While, ast.Try, ast.With)):
|
|
312
|
-
visit_node(child, depth + 1)
|
|
313
|
-
else:
|
|
314
|
-
visit_node(child, depth)
|
|
315
|
-
|
|
316
|
-
visit_node(tree)
|
|
317
|
-
return max_depth
|
|
318
|
-
except SyntaxError:
|
|
319
|
-
return 0
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
class TypeScriptMaintainabilityStrategy(MaintainabilityStrategy):
|
|
323
|
-
"""TypeScript-specific maintainability scoring strategy."""
|
|
324
|
-
|
|
325
|
-
def calculate(
|
|
326
|
-
self, code: str, file_path: Path | None = None, context: dict[str, Any] | None = None
|
|
327
|
-
) -> float:
|
|
328
|
-
"""Calculate maintainability for TypeScript code."""
|
|
329
|
-
lines = code.split("\n")
|
|
330
|
-
total_lines = len(lines)
|
|
331
|
-
|
|
332
|
-
if total_lines == 0:
|
|
333
|
-
return BASE_SCORE
|
|
334
|
-
|
|
335
|
-
# Base score - TypeScript has better type safety
|
|
336
|
-
is_typescript = file_path and file_path.suffix in [".ts", ".tsx"]
|
|
337
|
-
TYPESCRIPT_BASE_BOOST = 1.0 # TypeScript gets 1.0 point boost for type safety
|
|
338
|
-
score = BASE_SCORE + TYPESCRIPT_BASE_BOOST if is_typescript else BASE_SCORE
|
|
339
|
-
|
|
340
|
-
# Pattern analysis
|
|
341
|
-
patterns = self._analyze_patterns(code, is_typescript)
|
|
342
|
-
score += patterns["positive"]
|
|
343
|
-
score -= patterns["negative"]
|
|
344
|
-
|
|
345
|
-
# Structure analysis
|
|
346
|
-
structure = self._analyze_structure(code, total_lines)
|
|
347
|
-
score += structure["positive"]
|
|
348
|
-
score -= structure["negative"]
|
|
349
|
-
|
|
350
|
-
return max(0.0, min(10.0, score))
|
|
351
|
-
|
|
352
|
-
def _analyze_patterns(self, code: str, is_typescript: bool) -> dict[str, float]:
|
|
353
|
-
"""Analyze TypeScript/JavaScript patterns."""
|
|
354
|
-
positive = 0.0
|
|
355
|
-
negative = 0.0
|
|
356
|
-
|
|
357
|
-
# Type safety patterns
|
|
358
|
-
has_types = sum(
|
|
359
|
-
1
|
|
360
|
-
for line in code.split("\n")
|
|
361
|
-
if ": " in line
|
|
362
|
-
and (
|
|
363
|
-
"string" in line
|
|
364
|
-
or "number" in line
|
|
365
|
-
or "boolean" in line
|
|
366
|
-
or "object" in line
|
|
367
|
-
or "Array<" in line
|
|
368
|
-
or "Promise<" in line
|
|
369
|
-
or "Record<" in line
|
|
370
|
-
)
|
|
371
|
-
)
|
|
372
|
-
has_interfaces = len(re.findall(r"interface\s+\w+", code))
|
|
373
|
-
has_type_aliases = len(re.findall(r"type\s+\w+\s*=", code))
|
|
374
|
-
has_generics = len(re.findall(r"<\w+>", code))
|
|
375
|
-
has_const_assertions = bool(re.search(r"as const", code))
|
|
376
|
-
|
|
377
|
-
# Documentation
|
|
378
|
-
has_jsdoc = bool(re.search(r"/\*\*[\s\S]*?\*/", code))
|
|
379
|
-
has_comments = sum(
|
|
380
|
-
1 for line in code.split("\n") if line.strip().startswith("//") or "/*" in line
|
|
381
|
-
)
|
|
382
|
-
|
|
383
|
-
# Error handling
|
|
384
|
-
has_error_handling = bool(re.search(r"try\s*\{|catch\s*\(|throw\s+new\s+Error", code))
|
|
385
|
-
|
|
386
|
-
# Code organization
|
|
387
|
-
has_exports = len(re.findall(r"export\s+(const|function|class|interface|type)", code))
|
|
388
|
-
has_imports = len(re.findall(r"import\s+.*from\s+['\"]", code))
|
|
389
|
-
|
|
390
|
-
# Apply positive factors
|
|
391
|
-
if is_typescript:
|
|
392
|
-
if has_types > 0:
|
|
393
|
-
positive += min(has_types / len(code.split("\n")) * 2.5, 2.5)
|
|
394
|
-
if has_interfaces > 0:
|
|
395
|
-
positive += min(has_interfaces * 0.3, 1.0)
|
|
396
|
-
if has_type_aliases > 0:
|
|
397
|
-
positive += min(has_type_aliases * 0.2, 0.5)
|
|
398
|
-
if has_generics > 0:
|
|
399
|
-
positive += min(has_generics * 0.1, 0.5)
|
|
400
|
-
if has_const_assertions:
|
|
401
|
-
positive += 0.2
|
|
402
|
-
|
|
403
|
-
if has_jsdoc:
|
|
404
|
-
positive += 1.5
|
|
405
|
-
if has_comments > 0:
|
|
406
|
-
positive += min(has_comments / len(code.split("\n")) * 2, 2.0)
|
|
407
|
-
if has_error_handling:
|
|
408
|
-
positive += 0.5
|
|
409
|
-
if has_exports > 0:
|
|
410
|
-
positive += min(has_exports * 0.1, 0.5)
|
|
411
|
-
if has_imports > 0:
|
|
412
|
-
positive += min(has_imports * 0.05, 0.3)
|
|
413
|
-
|
|
414
|
-
# Negative factors
|
|
415
|
-
long_lines = sum(1 for line in code.split("\n") if len(line) > 120)
|
|
416
|
-
nesting_depth = self._calculate_nesting_depth(code)
|
|
417
|
-
function_count = len(re.findall(r"(function|const\s+\w+\s*=\s*\(|=>\s*\{)", code))
|
|
418
|
-
avg_function_length = len(code.split("\n")) / max(function_count, 1)
|
|
419
|
-
|
|
420
|
-
if long_lines > 0:
|
|
421
|
-
negative += min(long_lines / len(code.split("\n")) * 1.5, 1.5)
|
|
422
|
-
if nesting_depth > 3:
|
|
423
|
-
negative += min((nesting_depth - 3) * 0.5, 2.0)
|
|
424
|
-
if avg_function_length > 50:
|
|
425
|
-
negative += min((avg_function_length - 50) / 50 * 1.0, 1.5)
|
|
426
|
-
|
|
427
|
-
# Bad patterns
|
|
428
|
-
if re.search(r"any\s+", code):
|
|
429
|
-
negative += 0.5 # Use of 'any' reduces type safety
|
|
430
|
-
if re.search(r"eval\(|Function\(", code):
|
|
431
|
-
negative += 1.0 # Security/maintainability risk
|
|
432
|
-
|
|
433
|
-
return {"positive": positive, "negative": negative}
|
|
434
|
-
|
|
435
|
-
def _analyze_structure(self, code: str, total_lines: int) -> dict[str, float]:
|
|
436
|
-
"""Analyze code structure and organization."""
|
|
437
|
-
positive = 0.0
|
|
438
|
-
negative = 0.0
|
|
439
|
-
|
|
440
|
-
# Check for proper module structure
|
|
441
|
-
has_default_export = bool(re.search(r"export\s+default", code))
|
|
442
|
-
has_named_exports = len(re.findall(r"export\s+(const|function|class)", code)) > 0
|
|
443
|
-
|
|
444
|
-
if has_default_export or has_named_exports:
|
|
445
|
-
positive += 0.3
|
|
446
|
-
|
|
447
|
-
# Check for circular dependencies (heuristic)
|
|
448
|
-
imports = re.findall(r"import\s+.*from\s+['\"]([^'\"]+)['\"]", code)
|
|
449
|
-
if len(set(imports)) < len(imports):
|
|
450
|
-
negative += 0.5 # Potential circular dependencies
|
|
451
|
-
|
|
452
|
-
return {"positive": positive, "negative": negative}
|
|
453
|
-
|
|
454
|
-
def _calculate_nesting_depth(self, code: str) -> int:
|
|
455
|
-
"""Calculate maximum nesting depth (heuristic-based)."""
|
|
456
|
-
max_depth = 0
|
|
457
|
-
current_depth = 0
|
|
458
|
-
|
|
459
|
-
for line in code.split("\n"):
|
|
460
|
-
# Count opening braces
|
|
461
|
-
current_depth += line.count("{") - line.count("}")
|
|
462
|
-
max_depth = max(max_depth, current_depth)
|
|
463
|
-
|
|
464
|
-
return max_depth
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
class ReactMaintainabilityStrategy(MaintainabilityStrategy):
|
|
468
|
-
"""React-specific maintainability scoring strategy."""
|
|
469
|
-
|
|
470
|
-
def __init__(self, typescript_strategy: TypeScriptMaintainabilityStrategy | None = None):
|
|
471
|
-
"""Initialize with optional TypeScript strategy for composition."""
|
|
472
|
-
self.typescript_strategy = typescript_strategy or TypeScriptMaintainabilityStrategy()
|
|
473
|
-
|
|
474
|
-
def calculate(
|
|
475
|
-
self, code: str, file_path: Path | None = None, context: dict[str, Any] | None = None
|
|
476
|
-
) -> float:
|
|
477
|
-
"""Calculate maintainability for React code."""
|
|
478
|
-
# Start with TypeScript base score
|
|
479
|
-
base_score = self.typescript_strategy.calculate(code, file_path, context)
|
|
480
|
-
|
|
481
|
-
# Add React-specific maintainability boost
|
|
482
|
-
react_boost = self._calculate_react_maintainability_boost(code)
|
|
483
|
-
final_score = base_score + react_boost
|
|
484
|
-
|
|
485
|
-
return max(0.0, min(10.0, final_score))
|
|
486
|
-
|
|
487
|
-
def _calculate_react_maintainability_boost(self, code: str) -> float:
|
|
488
|
-
"""Calculate maintainability boost from React patterns (0-2.0)."""
|
|
489
|
-
boost = 0.0
|
|
490
|
-
|
|
491
|
-
# Type safety for props
|
|
492
|
-
if re.search(r"interface\s+\w+Props|type\s+\w+Props", code):
|
|
493
|
-
boost += 0.5
|
|
494
|
-
|
|
495
|
-
# Proper component structure
|
|
496
|
-
if re.search(
|
|
497
|
-
r"(export\s+)?(const|function)\s+\w+\s*[:=]\s*(\([^)]*\)\s*=>|React\.FC)", code
|
|
498
|
-
):
|
|
499
|
-
boost += 0.5
|
|
500
|
-
|
|
501
|
-
# Error handling
|
|
502
|
-
if re.search(r"try\s*\{|ErrorBoundary|componentDidCatch", code):
|
|
503
|
-
boost += 0.5
|
|
504
|
-
|
|
505
|
-
# Documentation (JSDoc)
|
|
506
|
-
if re.search(r"/\*\*.*\*/", code, re.DOTALL):
|
|
507
|
-
boost += 0.5
|
|
508
|
-
|
|
509
|
-
# Custom hooks pattern (good separation of concerns)
|
|
510
|
-
if re.search(r"function\s+use\w+\s*\(|const\s+use\w+\s*=", code):
|
|
511
|
-
boost += 0.3
|
|
512
|
-
|
|
513
|
-
# Proper state management
|
|
514
|
-
if re.search(r"useState\(|useReducer\(", code):
|
|
515
|
-
boost += 0.2
|
|
516
|
-
|
|
517
|
-
return min(boost, 2.0)
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
class MaintainabilityScorer:
|
|
521
|
-
"""
|
|
522
|
-
Context-aware maintainability scorer using Strategy pattern.
|
|
523
|
-
|
|
524
|
-
Automatically selects the appropriate strategy based on detected language.
|
|
525
|
-
"""
|
|
526
|
-
|
|
527
|
-
def __init__(self):
|
|
528
|
-
"""Initialize with language-specific strategies."""
|
|
529
|
-
self.strategies: dict[Language, MaintainabilityStrategy] = {
|
|
530
|
-
Language.PYTHON: PythonMaintainabilityStrategy(),
|
|
531
|
-
Language.TYPESCRIPT: TypeScriptMaintainabilityStrategy(),
|
|
532
|
-
Language.JAVASCRIPT: TypeScriptMaintainabilityStrategy(), # Use TS strategy for JS
|
|
533
|
-
Language.REACT: ReactMaintainabilityStrategy(),
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
def calculate(
|
|
537
|
-
self,
|
|
538
|
-
code: str,
|
|
539
|
-
language: Language,
|
|
540
|
-
file_path: Path | None = None,
|
|
541
|
-
context: dict[str, Any] | None = None,
|
|
542
|
-
) -> float:
|
|
543
|
-
"""
|
|
544
|
-
Calculate maintainability score for code in the given language.
|
|
545
|
-
|
|
546
|
-
Args:
|
|
547
|
-
code: Source code content
|
|
548
|
-
language: Detected language
|
|
549
|
-
file_path: Optional path to the file
|
|
550
|
-
context: Optional context (e.g., project structure, dependencies)
|
|
551
|
-
|
|
552
|
-
Returns:
|
|
553
|
-
Maintainability score (0-10)
|
|
554
|
-
"""
|
|
555
|
-
strategy = self.strategies.get(language)
|
|
556
|
-
if strategy:
|
|
557
|
-
return strategy.calculate(code, file_path, context)
|
|
558
|
-
|
|
559
|
-
# Fallback to Python strategy for unknown languages
|
|
560
|
-
return PythonMaintainabilityStrategy().calculate(code, file_path, context)
|
|
561
|
-
|
|
562
|
-
def get_issues(
|
|
563
|
-
self,
|
|
564
|
-
code: str,
|
|
565
|
-
language: Language,
|
|
566
|
-
file_path: Path | None = None,
|
|
567
|
-
context: dict[str, Any] | None = None,
|
|
568
|
-
) -> list[dict[str, Any]]:
|
|
569
|
-
"""
|
|
570
|
-
Get specific maintainability issues (Phase 2 - P0).
|
|
571
|
-
|
|
572
|
-
Returns list of issues with details like missing docstrings, long functions, etc.
|
|
573
|
-
|
|
574
|
-
Args:
|
|
575
|
-
code: Source code content
|
|
576
|
-
language: Detected language
|
|
577
|
-
file_path: Optional path to the file
|
|
578
|
-
context: Optional context
|
|
579
|
-
|
|
580
|
-
Returns:
|
|
581
|
-
List of maintainability issues with details
|
|
582
|
-
"""
|
|
583
|
-
strategy = self.strategies.get(language)
|
|
584
|
-
if strategy and hasattr(strategy, "get_issues"):
|
|
585
|
-
return strategy.get_issues(code, file_path, context)
|
|
586
|
-
|
|
587
|
-
# Fallback to Python strategy for unknown languages
|
|
588
|
-
if hasattr(PythonMaintainabilityStrategy(), "get_issues"):
|
|
589
|
-
return PythonMaintainabilityStrategy().get_issues(code, file_path, context)
|
|
590
|
-
|
|
591
|
-
# Return empty list if strategy doesn't support get_issues
|
|
592
|
-
return []
|
|
593
|
-
|
|
1
|
+
"""
|
|
2
|
+
Context-Aware Maintainability Scorer - Language-aware maintainability analysis
|
|
3
|
+
|
|
4
|
+
Phase 3.1: Context-Aware Scoring Algorithm
|
|
5
|
+
|
|
6
|
+
Uses Strategy pattern to provide language-specific maintainability scoring
|
|
7
|
+
with pattern recognition, complexity analysis, and code structure evaluation.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import ast
|
|
13
|
+
import re
|
|
14
|
+
from abc import ABC, abstractmethod
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from ...core.language_detector import Language
|
|
19
|
+
|
|
20
|
+
# Constants for maintainability scoring
|
|
21
|
+
MI_EXCELLENT_THRESHOLD = 80.0 # Maintainability Index threshold for excellent code
|
|
22
|
+
MI_POOR_THRESHOLD = 20.0 # Maintainability Index threshold for poor code
|
|
23
|
+
MI_SCALE_FACTOR = 10.0 # Scale factor to convert MI (0-100) to score (0-10)
|
|
24
|
+
MAX_NESTING_DEPTH_THRESHOLD = 3 # Nesting depth threshold before penalties
|
|
25
|
+
MAX_AVG_FUNCTION_LENGTH = 50 # Average function length threshold (lines)
|
|
26
|
+
MAX_LINE_LENGTH = 100 # Maximum line length before penalties (characters)
|
|
27
|
+
BASE_SCORE = 5.0 # Base score for neutral maintainability
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class MaintainabilityStrategy(ABC):
|
|
31
|
+
"""Base strategy for language-specific maintainability scoring."""
|
|
32
|
+
|
|
33
|
+
@abstractmethod
|
|
34
|
+
def calculate(
|
|
35
|
+
self, code: str, file_path: Path | None = None, context: dict[str, Any] | None = None
|
|
36
|
+
) -> float:
|
|
37
|
+
"""
|
|
38
|
+
Calculate maintainability score (0-10 scale, higher is better).
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
code: Source code content
|
|
42
|
+
file_path: Optional path to the file
|
|
43
|
+
context: Optional context (e.g., project structure, dependencies)
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
Maintainability score (0-10)
|
|
47
|
+
"""
|
|
48
|
+
raise NotImplementedError
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class PythonMaintainabilityStrategy(MaintainabilityStrategy):
|
|
52
|
+
"""Python-specific maintainability scoring strategy."""
|
|
53
|
+
|
|
54
|
+
def calculate(
|
|
55
|
+
self, code: str, file_path: Path | None = None, context: dict[str, Any] | None = None
|
|
56
|
+
) -> float:
|
|
57
|
+
"""Calculate maintainability for Python code."""
|
|
58
|
+
try:
|
|
59
|
+
# Try to use radon if available (more accurate)
|
|
60
|
+
try:
|
|
61
|
+
from radon.complexity import mi_visit
|
|
62
|
+
|
|
63
|
+
lines = code.splitlines()
|
|
64
|
+
mi_score = mi_visit(code, lines)
|
|
65
|
+
# Maintainability Index: 0-100 scale, convert to 0-10
|
|
66
|
+
# MI > 80 = good (10), MI < 20 = bad (0)
|
|
67
|
+
base_score = min(mi_score / MI_SCALE_FACTOR, 10.0)
|
|
68
|
+
except ImportError:
|
|
69
|
+
# Fallback to heuristic-based scoring
|
|
70
|
+
base_score = self._heuristic_score(code)
|
|
71
|
+
|
|
72
|
+
# Apply context-aware adjustments
|
|
73
|
+
adjustments = self._analyze_patterns(code, file_path)
|
|
74
|
+
final_score = base_score + adjustments
|
|
75
|
+
|
|
76
|
+
return max(0.0, min(10.0, final_score))
|
|
77
|
+
|
|
78
|
+
except SyntaxError:
|
|
79
|
+
return 0.0
|
|
80
|
+
except Exception:
|
|
81
|
+
return BASE_SCORE # Neutral on error
|
|
82
|
+
|
|
83
|
+
def get_issues(
|
|
84
|
+
self, code: str, file_path: Path | None = None, context: dict[str, Any] | None = None
|
|
85
|
+
) -> list[dict[str, Any]]:
|
|
86
|
+
"""
|
|
87
|
+
Get specific maintainability issues (Phase 2 - P0).
|
|
88
|
+
|
|
89
|
+
Returns list of issues with details like missing docstrings, long functions, etc.
|
|
90
|
+
"""
|
|
91
|
+
from .issue_tracking import MaintainabilityIssue
|
|
92
|
+
|
|
93
|
+
issues: list[MaintainabilityIssue] = []
|
|
94
|
+
code_lines = code.splitlines()
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
tree = ast.parse(code)
|
|
98
|
+
except SyntaxError:
|
|
99
|
+
return [MaintainabilityIssue(
|
|
100
|
+
issue_type="syntax_error",
|
|
101
|
+
message="File contains syntax errors",
|
|
102
|
+
severity="high"
|
|
103
|
+
).__dict__]
|
|
104
|
+
|
|
105
|
+
# Check for missing docstrings in functions and classes
|
|
106
|
+
for node in ast.walk(tree):
|
|
107
|
+
if isinstance(node, ast.FunctionDef):
|
|
108
|
+
# Check if function has docstring
|
|
109
|
+
if not ast.get_docstring(node):
|
|
110
|
+
issues.append(MaintainabilityIssue(
|
|
111
|
+
issue_type="missing_docstring",
|
|
112
|
+
message=f"Function '{node.name}' is missing a docstring",
|
|
113
|
+
line_number=node.lineno,
|
|
114
|
+
function_name=node.name,
|
|
115
|
+
severity="medium",
|
|
116
|
+
suggestion="Add a docstring explaining the function's purpose, parameters, and return value"
|
|
117
|
+
))
|
|
118
|
+
|
|
119
|
+
# Check function length
|
|
120
|
+
if hasattr(node, "end_lineno") and node.end_lineno:
|
|
121
|
+
func_length = node.end_lineno - node.lineno
|
|
122
|
+
else:
|
|
123
|
+
# Estimate function length
|
|
124
|
+
func_length = len(code_lines[node.lineno - 1:node.lineno + 50]) if node.lineno <= len(code_lines) else 0
|
|
125
|
+
|
|
126
|
+
if func_length > MAX_AVG_FUNCTION_LENGTH:
|
|
127
|
+
severity = "high" if func_length > 100 else "medium"
|
|
128
|
+
issues.append(MaintainabilityIssue(
|
|
129
|
+
issue_type="long_function",
|
|
130
|
+
message=f"Function '{node.name}' is {func_length} lines long (threshold: {MAX_AVG_FUNCTION_LENGTH})",
|
|
131
|
+
line_number=node.lineno,
|
|
132
|
+
function_name=node.name,
|
|
133
|
+
metric_value=float(func_length),
|
|
134
|
+
threshold=float(MAX_AVG_FUNCTION_LENGTH),
|
|
135
|
+
severity=severity,
|
|
136
|
+
suggestion=f"Extract helper methods or break into smaller functions (target: <{MAX_AVG_FUNCTION_LENGTH} lines)"
|
|
137
|
+
))
|
|
138
|
+
|
|
139
|
+
# Check nesting depth
|
|
140
|
+
max_nesting = self._get_max_nesting_depth(node)
|
|
141
|
+
if max_nesting > MAX_NESTING_DEPTH_THRESHOLD:
|
|
142
|
+
severity = "high" if max_nesting > 6 else "medium"
|
|
143
|
+
issues.append(MaintainabilityIssue(
|
|
144
|
+
issue_type="deep_nesting",
|
|
145
|
+
message=f"Function '{node.name}' has {max_nesting} levels of nesting (threshold: {MAX_NESTING_DEPTH_THRESHOLD})",
|
|
146
|
+
line_number=node.lineno,
|
|
147
|
+
function_name=node.name,
|
|
148
|
+
metric_value=float(max_nesting),
|
|
149
|
+
threshold=float(MAX_NESTING_DEPTH_THRESHOLD),
|
|
150
|
+
severity=severity,
|
|
151
|
+
suggestion="Extract nested logic into separate functions to reduce complexity"
|
|
152
|
+
))
|
|
153
|
+
|
|
154
|
+
elif isinstance(node, ast.ClassDef):
|
|
155
|
+
# Check if class has docstring
|
|
156
|
+
if not ast.get_docstring(node):
|
|
157
|
+
issues.append(MaintainabilityIssue(
|
|
158
|
+
issue_type="missing_docstring",
|
|
159
|
+
message=f"Class '{node.name}' is missing a docstring",
|
|
160
|
+
line_number=node.lineno,
|
|
161
|
+
class_name=node.name,
|
|
162
|
+
severity="medium",
|
|
163
|
+
suggestion="Add a docstring explaining the class's purpose and usage"
|
|
164
|
+
))
|
|
165
|
+
|
|
166
|
+
# Check for long lines
|
|
167
|
+
for i, line in enumerate(code_lines, 1):
|
|
168
|
+
if len(line) > MAX_LINE_LENGTH:
|
|
169
|
+
issues.append(MaintainabilityIssue(
|
|
170
|
+
issue_type="long_line",
|
|
171
|
+
message=f"Line {i} is {len(line)} characters long (threshold: {MAX_LINE_LENGTH})",
|
|
172
|
+
line_number=i,
|
|
173
|
+
metric_value=float(len(line)),
|
|
174
|
+
threshold=float(MAX_LINE_LENGTH),
|
|
175
|
+
severity="low",
|
|
176
|
+
suggestion=f"Break long line into multiple lines or extract to variable"
|
|
177
|
+
))
|
|
178
|
+
|
|
179
|
+
# Check for missing type hints in function signatures
|
|
180
|
+
for node in ast.walk(tree):
|
|
181
|
+
if isinstance(node, ast.FunctionDef):
|
|
182
|
+
# Check if return type annotation is missing
|
|
183
|
+
if node.returns is None:
|
|
184
|
+
issues.append(MaintainabilityIssue(
|
|
185
|
+
issue_type="missing_type_hint",
|
|
186
|
+
message=f"Function '{node.name}' is missing return type annotation",
|
|
187
|
+
line_number=node.lineno,
|
|
188
|
+
function_name=node.name,
|
|
189
|
+
severity="low",
|
|
190
|
+
suggestion="Add return type annotation (e.g., -> str, -> int, -> None)"
|
|
191
|
+
))
|
|
192
|
+
|
|
193
|
+
# Check if all parameters have type hints
|
|
194
|
+
params_without_hints = [arg.arg for arg in node.args.args if arg.annotation is None]
|
|
195
|
+
if params_without_hints:
|
|
196
|
+
issues.append(MaintainabilityIssue(
|
|
197
|
+
issue_type="missing_type_hints",
|
|
198
|
+
message=f"Function '{node.name}' parameters missing type hints: {', '.join(params_without_hints)}",
|
|
199
|
+
line_number=node.lineno,
|
|
200
|
+
function_name=node.name,
|
|
201
|
+
severity="low",
|
|
202
|
+
suggestion="Add type annotations to all function parameters"
|
|
203
|
+
))
|
|
204
|
+
|
|
205
|
+
# Convert to dict format
|
|
206
|
+
return [
|
|
207
|
+
{
|
|
208
|
+
"issue_type": issue.issue_type,
|
|
209
|
+
"message": issue.message,
|
|
210
|
+
"line_number": issue.line_number,
|
|
211
|
+
"severity": issue.severity,
|
|
212
|
+
"suggestion": issue.suggestion,
|
|
213
|
+
"function_name": issue.function_name,
|
|
214
|
+
"class_name": issue.class_name,
|
|
215
|
+
"metric_value": issue.metric_value,
|
|
216
|
+
"threshold": issue.threshold,
|
|
217
|
+
}
|
|
218
|
+
for issue in issues
|
|
219
|
+
]
|
|
220
|
+
|
|
221
|
+
def _get_max_nesting_depth(self, node: ast.AST, current_depth: int = 0) -> int:
|
|
222
|
+
"""Calculate maximum nesting depth in an AST node."""
|
|
223
|
+
max_depth = current_depth
|
|
224
|
+
for child in ast.iter_child_nodes(node):
|
|
225
|
+
if isinstance(child, (ast.If, ast.For, ast.While, ast.Try, ast.With)):
|
|
226
|
+
child_depth = self._get_max_nesting_depth(child, current_depth + 1)
|
|
227
|
+
max_depth = max(max_depth, child_depth)
|
|
228
|
+
else:
|
|
229
|
+
child_depth = self._get_max_nesting_depth(child, current_depth)
|
|
230
|
+
max_depth = max(max_depth, child_depth)
|
|
231
|
+
return max_depth
|
|
232
|
+
|
|
233
|
+
def _heuristic_score(self, code: str) -> float:
|
|
234
|
+
"""Heuristic-based scoring when radon is not available."""
|
|
235
|
+
lines = code.split("\n")
|
|
236
|
+
total_lines = len(lines)
|
|
237
|
+
|
|
238
|
+
if total_lines == 0:
|
|
239
|
+
return BASE_SCORE
|
|
240
|
+
|
|
241
|
+
score = BASE_SCORE # Base score
|
|
242
|
+
|
|
243
|
+
# Positive factors
|
|
244
|
+
has_docstrings = bool(re.search(r'""".*?"""', code, re.DOTALL))
|
|
245
|
+
has_type_hints = bool(re.search(r"def\s+\w+\s*\([^)]*:\s*(str|int|float|bool|list|dict)", code))
|
|
246
|
+
has_async = bool(re.search(r"async\s+def|await\s+", code))
|
|
247
|
+
has_error_handling = bool(re.search(r"try\s*:|except\s+", code))
|
|
248
|
+
has_dataclasses = bool(re.search(r"@dataclass|from dataclasses import", code))
|
|
249
|
+
has_pydantic = bool(re.search(r"from pydantic import|BaseModel", code))
|
|
250
|
+
|
|
251
|
+
# Negative factors
|
|
252
|
+
long_lines = sum(1 for line in lines if len(line) > MAX_LINE_LENGTH)
|
|
253
|
+
nesting_depth = self._calculate_nesting_depth(code)
|
|
254
|
+
function_count = len(re.findall(r"def\s+\w+\s*\(", code))
|
|
255
|
+
avg_function_length = total_lines / max(function_count, 1)
|
|
256
|
+
|
|
257
|
+
# Apply adjustments
|
|
258
|
+
if has_docstrings:
|
|
259
|
+
score += 1.0
|
|
260
|
+
if has_type_hints:
|
|
261
|
+
score += 1.5
|
|
262
|
+
if has_async:
|
|
263
|
+
score += 0.5
|
|
264
|
+
if has_error_handling:
|
|
265
|
+
score += 0.5
|
|
266
|
+
if has_dataclasses or has_pydantic:
|
|
267
|
+
score += 0.5
|
|
268
|
+
|
|
269
|
+
if long_lines > 0:
|
|
270
|
+
score -= min(long_lines / total_lines * 1.5, 1.5)
|
|
271
|
+
if nesting_depth > MAX_NESTING_DEPTH_THRESHOLD:
|
|
272
|
+
score -= min((nesting_depth - MAX_NESTING_DEPTH_THRESHOLD) * 0.5, 2.0)
|
|
273
|
+
if avg_function_length > MAX_AVG_FUNCTION_LENGTH:
|
|
274
|
+
score -= min((avg_function_length - MAX_AVG_FUNCTION_LENGTH) / MAX_AVG_FUNCTION_LENGTH * 1.0, 1.5)
|
|
275
|
+
|
|
276
|
+
return max(0.0, min(10.0, score))
|
|
277
|
+
|
|
278
|
+
def _analyze_patterns(self, code: str, file_path: Path | None = None) -> float:
|
|
279
|
+
"""Analyze Python-specific patterns for maintainability adjustments."""
|
|
280
|
+
adjustments = 0.0
|
|
281
|
+
|
|
282
|
+
# Good patterns
|
|
283
|
+
if re.search(r"@lru_cache|@functools\.lru_cache", code):
|
|
284
|
+
adjustments += 0.3 # Caching improves maintainability
|
|
285
|
+
if re.search(r"from typing import|from __future__ import annotations", code):
|
|
286
|
+
adjustments += 0.2 # Type hints improve maintainability
|
|
287
|
+
if re.search(r"class\s+\w+\(.*BaseModel|class\s+\w+\(.*Pydantic", code):
|
|
288
|
+
adjustments += 0.3 # Pydantic models improve structure
|
|
289
|
+
if re.search(r"async def|asyncio\.|TaskGroup", code):
|
|
290
|
+
adjustments += 0.2 # Modern async patterns
|
|
291
|
+
|
|
292
|
+
# Bad patterns (penalties)
|
|
293
|
+
if re.search(r"eval\(|exec\(|__import__\(", code):
|
|
294
|
+
adjustments -= 1.0 # Security/maintainability risk
|
|
295
|
+
if re.search(r"global\s+\w+", code):
|
|
296
|
+
adjustments -= 0.3 # Global state reduces maintainability
|
|
297
|
+
|
|
298
|
+
return adjustments
|
|
299
|
+
|
|
300
|
+
def _calculate_nesting_depth(self, code: str) -> int:
|
|
301
|
+
"""Calculate maximum nesting depth in code."""
|
|
302
|
+
try:
|
|
303
|
+
tree = ast.parse(code)
|
|
304
|
+
max_depth = 0
|
|
305
|
+
|
|
306
|
+
def visit_node(node: ast.AST, depth: int = 0) -> None:
|
|
307
|
+
nonlocal max_depth
|
|
308
|
+
max_depth = max(max_depth, depth)
|
|
309
|
+
|
|
310
|
+
for child in ast.iter_child_nodes(node):
|
|
311
|
+
if isinstance(child, (ast.If, ast.For, ast.While, ast.Try, ast.With)):
|
|
312
|
+
visit_node(child, depth + 1)
|
|
313
|
+
else:
|
|
314
|
+
visit_node(child, depth)
|
|
315
|
+
|
|
316
|
+
visit_node(tree)
|
|
317
|
+
return max_depth
|
|
318
|
+
except SyntaxError:
|
|
319
|
+
return 0
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
class TypeScriptMaintainabilityStrategy(MaintainabilityStrategy):
|
|
323
|
+
"""TypeScript-specific maintainability scoring strategy."""
|
|
324
|
+
|
|
325
|
+
def calculate(
|
|
326
|
+
self, code: str, file_path: Path | None = None, context: dict[str, Any] | None = None
|
|
327
|
+
) -> float:
|
|
328
|
+
"""Calculate maintainability for TypeScript code."""
|
|
329
|
+
lines = code.split("\n")
|
|
330
|
+
total_lines = len(lines)
|
|
331
|
+
|
|
332
|
+
if total_lines == 0:
|
|
333
|
+
return BASE_SCORE
|
|
334
|
+
|
|
335
|
+
# Base score - TypeScript has better type safety
|
|
336
|
+
is_typescript = file_path and file_path.suffix in [".ts", ".tsx"]
|
|
337
|
+
TYPESCRIPT_BASE_BOOST = 1.0 # TypeScript gets 1.0 point boost for type safety
|
|
338
|
+
score = BASE_SCORE + TYPESCRIPT_BASE_BOOST if is_typescript else BASE_SCORE
|
|
339
|
+
|
|
340
|
+
# Pattern analysis
|
|
341
|
+
patterns = self._analyze_patterns(code, is_typescript)
|
|
342
|
+
score += patterns["positive"]
|
|
343
|
+
score -= patterns["negative"]
|
|
344
|
+
|
|
345
|
+
# Structure analysis
|
|
346
|
+
structure = self._analyze_structure(code, total_lines)
|
|
347
|
+
score += structure["positive"]
|
|
348
|
+
score -= structure["negative"]
|
|
349
|
+
|
|
350
|
+
return max(0.0, min(10.0, score))
|
|
351
|
+
|
|
352
|
+
def _analyze_patterns(self, code: str, is_typescript: bool) -> dict[str, float]:
|
|
353
|
+
"""Analyze TypeScript/JavaScript patterns."""
|
|
354
|
+
positive = 0.0
|
|
355
|
+
negative = 0.0
|
|
356
|
+
|
|
357
|
+
# Type safety patterns
|
|
358
|
+
has_types = sum(
|
|
359
|
+
1
|
|
360
|
+
for line in code.split("\n")
|
|
361
|
+
if ": " in line
|
|
362
|
+
and (
|
|
363
|
+
"string" in line
|
|
364
|
+
or "number" in line
|
|
365
|
+
or "boolean" in line
|
|
366
|
+
or "object" in line
|
|
367
|
+
or "Array<" in line
|
|
368
|
+
or "Promise<" in line
|
|
369
|
+
or "Record<" in line
|
|
370
|
+
)
|
|
371
|
+
)
|
|
372
|
+
has_interfaces = len(re.findall(r"interface\s+\w+", code))
|
|
373
|
+
has_type_aliases = len(re.findall(r"type\s+\w+\s*=", code))
|
|
374
|
+
has_generics = len(re.findall(r"<\w+>", code))
|
|
375
|
+
has_const_assertions = bool(re.search(r"as const", code))
|
|
376
|
+
|
|
377
|
+
# Documentation
|
|
378
|
+
has_jsdoc = bool(re.search(r"/\*\*[\s\S]*?\*/", code))
|
|
379
|
+
has_comments = sum(
|
|
380
|
+
1 for line in code.split("\n") if line.strip().startswith("//") or "/*" in line
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
# Error handling
|
|
384
|
+
has_error_handling = bool(re.search(r"try\s*\{|catch\s*\(|throw\s+new\s+Error", code))
|
|
385
|
+
|
|
386
|
+
# Code organization
|
|
387
|
+
has_exports = len(re.findall(r"export\s+(const|function|class|interface|type)", code))
|
|
388
|
+
has_imports = len(re.findall(r"import\s+.*from\s+['\"]", code))
|
|
389
|
+
|
|
390
|
+
# Apply positive factors
|
|
391
|
+
if is_typescript:
|
|
392
|
+
if has_types > 0:
|
|
393
|
+
positive += min(has_types / len(code.split("\n")) * 2.5, 2.5)
|
|
394
|
+
if has_interfaces > 0:
|
|
395
|
+
positive += min(has_interfaces * 0.3, 1.0)
|
|
396
|
+
if has_type_aliases > 0:
|
|
397
|
+
positive += min(has_type_aliases * 0.2, 0.5)
|
|
398
|
+
if has_generics > 0:
|
|
399
|
+
positive += min(has_generics * 0.1, 0.5)
|
|
400
|
+
if has_const_assertions:
|
|
401
|
+
positive += 0.2
|
|
402
|
+
|
|
403
|
+
if has_jsdoc:
|
|
404
|
+
positive += 1.5
|
|
405
|
+
if has_comments > 0:
|
|
406
|
+
positive += min(has_comments / len(code.split("\n")) * 2, 2.0)
|
|
407
|
+
if has_error_handling:
|
|
408
|
+
positive += 0.5
|
|
409
|
+
if has_exports > 0:
|
|
410
|
+
positive += min(has_exports * 0.1, 0.5)
|
|
411
|
+
if has_imports > 0:
|
|
412
|
+
positive += min(has_imports * 0.05, 0.3)
|
|
413
|
+
|
|
414
|
+
# Negative factors
|
|
415
|
+
long_lines = sum(1 for line in code.split("\n") if len(line) > 120)
|
|
416
|
+
nesting_depth = self._calculate_nesting_depth(code)
|
|
417
|
+
function_count = len(re.findall(r"(function|const\s+\w+\s*=\s*\(|=>\s*\{)", code))
|
|
418
|
+
avg_function_length = len(code.split("\n")) / max(function_count, 1)
|
|
419
|
+
|
|
420
|
+
if long_lines > 0:
|
|
421
|
+
negative += min(long_lines / len(code.split("\n")) * 1.5, 1.5)
|
|
422
|
+
if nesting_depth > 3:
|
|
423
|
+
negative += min((nesting_depth - 3) * 0.5, 2.0)
|
|
424
|
+
if avg_function_length > 50:
|
|
425
|
+
negative += min((avg_function_length - 50) / 50 * 1.0, 1.5)
|
|
426
|
+
|
|
427
|
+
# Bad patterns
|
|
428
|
+
if re.search(r"any\s+", code):
|
|
429
|
+
negative += 0.5 # Use of 'any' reduces type safety
|
|
430
|
+
if re.search(r"eval\(|Function\(", code):
|
|
431
|
+
negative += 1.0 # Security/maintainability risk
|
|
432
|
+
|
|
433
|
+
return {"positive": positive, "negative": negative}
|
|
434
|
+
|
|
435
|
+
def _analyze_structure(self, code: str, total_lines: int) -> dict[str, float]:
|
|
436
|
+
"""Analyze code structure and organization."""
|
|
437
|
+
positive = 0.0
|
|
438
|
+
negative = 0.0
|
|
439
|
+
|
|
440
|
+
# Check for proper module structure
|
|
441
|
+
has_default_export = bool(re.search(r"export\s+default", code))
|
|
442
|
+
has_named_exports = len(re.findall(r"export\s+(const|function|class)", code)) > 0
|
|
443
|
+
|
|
444
|
+
if has_default_export or has_named_exports:
|
|
445
|
+
positive += 0.3
|
|
446
|
+
|
|
447
|
+
# Check for circular dependencies (heuristic)
|
|
448
|
+
imports = re.findall(r"import\s+.*from\s+['\"]([^'\"]+)['\"]", code)
|
|
449
|
+
if len(set(imports)) < len(imports):
|
|
450
|
+
negative += 0.5 # Potential circular dependencies
|
|
451
|
+
|
|
452
|
+
return {"positive": positive, "negative": negative}
|
|
453
|
+
|
|
454
|
+
def _calculate_nesting_depth(self, code: str) -> int:
|
|
455
|
+
"""Calculate maximum nesting depth (heuristic-based)."""
|
|
456
|
+
max_depth = 0
|
|
457
|
+
current_depth = 0
|
|
458
|
+
|
|
459
|
+
for line in code.split("\n"):
|
|
460
|
+
# Count opening braces
|
|
461
|
+
current_depth += line.count("{") - line.count("}")
|
|
462
|
+
max_depth = max(max_depth, current_depth)
|
|
463
|
+
|
|
464
|
+
return max_depth
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
class ReactMaintainabilityStrategy(MaintainabilityStrategy):
|
|
468
|
+
"""React-specific maintainability scoring strategy."""
|
|
469
|
+
|
|
470
|
+
def __init__(self, typescript_strategy: TypeScriptMaintainabilityStrategy | None = None):
|
|
471
|
+
"""Initialize with optional TypeScript strategy for composition."""
|
|
472
|
+
self.typescript_strategy = typescript_strategy or TypeScriptMaintainabilityStrategy()
|
|
473
|
+
|
|
474
|
+
def calculate(
|
|
475
|
+
self, code: str, file_path: Path | None = None, context: dict[str, Any] | None = None
|
|
476
|
+
) -> float:
|
|
477
|
+
"""Calculate maintainability for React code."""
|
|
478
|
+
# Start with TypeScript base score
|
|
479
|
+
base_score = self.typescript_strategy.calculate(code, file_path, context)
|
|
480
|
+
|
|
481
|
+
# Add React-specific maintainability boost
|
|
482
|
+
react_boost = self._calculate_react_maintainability_boost(code)
|
|
483
|
+
final_score = base_score + react_boost
|
|
484
|
+
|
|
485
|
+
return max(0.0, min(10.0, final_score))
|
|
486
|
+
|
|
487
|
+
def _calculate_react_maintainability_boost(self, code: str) -> float:
|
|
488
|
+
"""Calculate maintainability boost from React patterns (0-2.0)."""
|
|
489
|
+
boost = 0.0
|
|
490
|
+
|
|
491
|
+
# Type safety for props
|
|
492
|
+
if re.search(r"interface\s+\w+Props|type\s+\w+Props", code):
|
|
493
|
+
boost += 0.5
|
|
494
|
+
|
|
495
|
+
# Proper component structure
|
|
496
|
+
if re.search(
|
|
497
|
+
r"(export\s+)?(const|function)\s+\w+\s*[:=]\s*(\([^)]*\)\s*=>|React\.FC)", code
|
|
498
|
+
):
|
|
499
|
+
boost += 0.5
|
|
500
|
+
|
|
501
|
+
# Error handling
|
|
502
|
+
if re.search(r"try\s*\{|ErrorBoundary|componentDidCatch", code):
|
|
503
|
+
boost += 0.5
|
|
504
|
+
|
|
505
|
+
# Documentation (JSDoc)
|
|
506
|
+
if re.search(r"/\*\*.*\*/", code, re.DOTALL):
|
|
507
|
+
boost += 0.5
|
|
508
|
+
|
|
509
|
+
# Custom hooks pattern (good separation of concerns)
|
|
510
|
+
if re.search(r"function\s+use\w+\s*\(|const\s+use\w+\s*=", code):
|
|
511
|
+
boost += 0.3
|
|
512
|
+
|
|
513
|
+
# Proper state management
|
|
514
|
+
if re.search(r"useState\(|useReducer\(", code):
|
|
515
|
+
boost += 0.2
|
|
516
|
+
|
|
517
|
+
return min(boost, 2.0)
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
class MaintainabilityScorer:
|
|
521
|
+
"""
|
|
522
|
+
Context-aware maintainability scorer using Strategy pattern.
|
|
523
|
+
|
|
524
|
+
Automatically selects the appropriate strategy based on detected language.
|
|
525
|
+
"""
|
|
526
|
+
|
|
527
|
+
def __init__(self):
|
|
528
|
+
"""Initialize with language-specific strategies."""
|
|
529
|
+
self.strategies: dict[Language, MaintainabilityStrategy] = {
|
|
530
|
+
Language.PYTHON: PythonMaintainabilityStrategy(),
|
|
531
|
+
Language.TYPESCRIPT: TypeScriptMaintainabilityStrategy(),
|
|
532
|
+
Language.JAVASCRIPT: TypeScriptMaintainabilityStrategy(), # Use TS strategy for JS
|
|
533
|
+
Language.REACT: ReactMaintainabilityStrategy(),
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
def calculate(
|
|
537
|
+
self,
|
|
538
|
+
code: str,
|
|
539
|
+
language: Language,
|
|
540
|
+
file_path: Path | None = None,
|
|
541
|
+
context: dict[str, Any] | None = None,
|
|
542
|
+
) -> float:
|
|
543
|
+
"""
|
|
544
|
+
Calculate maintainability score for code in the given language.
|
|
545
|
+
|
|
546
|
+
Args:
|
|
547
|
+
code: Source code content
|
|
548
|
+
language: Detected language
|
|
549
|
+
file_path: Optional path to the file
|
|
550
|
+
context: Optional context (e.g., project structure, dependencies)
|
|
551
|
+
|
|
552
|
+
Returns:
|
|
553
|
+
Maintainability score (0-10)
|
|
554
|
+
"""
|
|
555
|
+
strategy = self.strategies.get(language)
|
|
556
|
+
if strategy:
|
|
557
|
+
return strategy.calculate(code, file_path, context)
|
|
558
|
+
|
|
559
|
+
# Fallback to Python strategy for unknown languages
|
|
560
|
+
return PythonMaintainabilityStrategy().calculate(code, file_path, context)
|
|
561
|
+
|
|
562
|
+
def get_issues(
|
|
563
|
+
self,
|
|
564
|
+
code: str,
|
|
565
|
+
language: Language,
|
|
566
|
+
file_path: Path | None = None,
|
|
567
|
+
context: dict[str, Any] | None = None,
|
|
568
|
+
) -> list[dict[str, Any]]:
|
|
569
|
+
"""
|
|
570
|
+
Get specific maintainability issues (Phase 2 - P0).
|
|
571
|
+
|
|
572
|
+
Returns list of issues with details like missing docstrings, long functions, etc.
|
|
573
|
+
|
|
574
|
+
Args:
|
|
575
|
+
code: Source code content
|
|
576
|
+
language: Detected language
|
|
577
|
+
file_path: Optional path to the file
|
|
578
|
+
context: Optional context
|
|
579
|
+
|
|
580
|
+
Returns:
|
|
581
|
+
List of maintainability issues with details
|
|
582
|
+
"""
|
|
583
|
+
strategy = self.strategies.get(language)
|
|
584
|
+
if strategy and hasattr(strategy, "get_issues"):
|
|
585
|
+
return strategy.get_issues(code, file_path, context)
|
|
586
|
+
|
|
587
|
+
# Fallback to Python strategy for unknown languages
|
|
588
|
+
if hasattr(PythonMaintainabilityStrategy(), "get_issues"):
|
|
589
|
+
return PythonMaintainabilityStrategy().get_issues(code, file_path, context)
|
|
590
|
+
|
|
591
|
+
# Return empty list if strategy doesn't support get_issues
|
|
592
|
+
return []
|
|
593
|
+
|