attune-ai 2.0.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.
- attune/__init__.py +358 -0
- attune/adaptive/__init__.py +13 -0
- attune/adaptive/task_complexity.py +127 -0
- attune/agent_monitoring.py +414 -0
- attune/cache/__init__.py +117 -0
- attune/cache/base.py +166 -0
- attune/cache/dependency_manager.py +256 -0
- attune/cache/hash_only.py +251 -0
- attune/cache/hybrid.py +457 -0
- attune/cache/storage.py +285 -0
- attune/cache_monitor.py +356 -0
- attune/cache_stats.py +298 -0
- attune/cli/__init__.py +152 -0
- attune/cli/__main__.py +12 -0
- attune/cli/commands/__init__.py +1 -0
- attune/cli/commands/batch.py +264 -0
- attune/cli/commands/cache.py +248 -0
- attune/cli/commands/help.py +331 -0
- attune/cli/commands/info.py +140 -0
- attune/cli/commands/inspect.py +436 -0
- attune/cli/commands/inspection.py +57 -0
- attune/cli/commands/memory.py +48 -0
- attune/cli/commands/metrics.py +92 -0
- attune/cli/commands/orchestrate.py +184 -0
- attune/cli/commands/patterns.py +207 -0
- attune/cli/commands/profiling.py +202 -0
- attune/cli/commands/provider.py +98 -0
- attune/cli/commands/routing.py +285 -0
- attune/cli/commands/setup.py +96 -0
- attune/cli/commands/status.py +235 -0
- attune/cli/commands/sync.py +166 -0
- attune/cli/commands/tier.py +121 -0
- attune/cli/commands/utilities.py +114 -0
- attune/cli/commands/workflow.py +579 -0
- attune/cli/core.py +32 -0
- attune/cli/parsers/__init__.py +68 -0
- attune/cli/parsers/batch.py +118 -0
- attune/cli/parsers/cache.py +65 -0
- attune/cli/parsers/help.py +41 -0
- attune/cli/parsers/info.py +26 -0
- attune/cli/parsers/inspect.py +66 -0
- attune/cli/parsers/metrics.py +42 -0
- attune/cli/parsers/orchestrate.py +61 -0
- attune/cli/parsers/patterns.py +54 -0
- attune/cli/parsers/provider.py +40 -0
- attune/cli/parsers/routing.py +110 -0
- attune/cli/parsers/setup.py +42 -0
- attune/cli/parsers/status.py +47 -0
- attune/cli/parsers/sync.py +31 -0
- attune/cli/parsers/tier.py +33 -0
- attune/cli/parsers/workflow.py +77 -0
- attune/cli/utils/__init__.py +1 -0
- attune/cli/utils/data.py +242 -0
- attune/cli/utils/helpers.py +68 -0
- attune/cli_legacy.py +3957 -0
- attune/cli_minimal.py +1159 -0
- attune/cli_router.py +437 -0
- attune/cli_unified.py +814 -0
- attune/config/__init__.py +66 -0
- attune/config/xml_config.py +286 -0
- attune/config.py +545 -0
- attune/coordination.py +870 -0
- attune/core.py +1511 -0
- attune/core_modules/__init__.py +15 -0
- attune/cost_tracker.py +626 -0
- attune/dashboard/__init__.py +41 -0
- attune/dashboard/app.py +512 -0
- attune/dashboard/simple_server.py +435 -0
- attune/dashboard/standalone_server.py +547 -0
- attune/discovery.py +306 -0
- attune/emergence.py +306 -0
- attune/exceptions.py +123 -0
- attune/feedback_loops.py +373 -0
- attune/hot_reload/README.md +473 -0
- attune/hot_reload/__init__.py +62 -0
- attune/hot_reload/config.py +83 -0
- attune/hot_reload/integration.py +229 -0
- attune/hot_reload/reloader.py +298 -0
- attune/hot_reload/watcher.py +183 -0
- attune/hot_reload/websocket.py +177 -0
- attune/levels.py +577 -0
- attune/leverage_points.py +441 -0
- attune/logging_config.py +261 -0
- attune/mcp/__init__.py +10 -0
- attune/mcp/server.py +506 -0
- attune/memory/__init__.py +237 -0
- attune/memory/claude_memory.py +469 -0
- attune/memory/config.py +224 -0
- attune/memory/control_panel.py +1290 -0
- attune/memory/control_panel_support.py +145 -0
- attune/memory/cross_session.py +845 -0
- attune/memory/edges.py +179 -0
- attune/memory/encryption.py +159 -0
- attune/memory/file_session.py +770 -0
- attune/memory/graph.py +570 -0
- attune/memory/long_term.py +913 -0
- attune/memory/long_term_types.py +99 -0
- attune/memory/mixins/__init__.py +25 -0
- attune/memory/mixins/backend_init_mixin.py +249 -0
- attune/memory/mixins/capabilities_mixin.py +208 -0
- attune/memory/mixins/handoff_mixin.py +208 -0
- attune/memory/mixins/lifecycle_mixin.py +49 -0
- attune/memory/mixins/long_term_mixin.py +352 -0
- attune/memory/mixins/promotion_mixin.py +109 -0
- attune/memory/mixins/short_term_mixin.py +182 -0
- attune/memory/nodes.py +179 -0
- attune/memory/redis_bootstrap.py +540 -0
- attune/memory/security/__init__.py +31 -0
- attune/memory/security/audit_logger.py +932 -0
- attune/memory/security/pii_scrubber.py +640 -0
- attune/memory/security/secrets_detector.py +678 -0
- attune/memory/short_term.py +2192 -0
- attune/memory/simple_storage.py +302 -0
- attune/memory/storage/__init__.py +15 -0
- attune/memory/storage_backend.py +167 -0
- attune/memory/summary_index.py +583 -0
- attune/memory/types.py +446 -0
- attune/memory/unified.py +182 -0
- attune/meta_workflows/__init__.py +74 -0
- attune/meta_workflows/agent_creator.py +248 -0
- attune/meta_workflows/builtin_templates.py +567 -0
- attune/meta_workflows/cli_commands/__init__.py +56 -0
- attune/meta_workflows/cli_commands/agent_commands.py +321 -0
- attune/meta_workflows/cli_commands/analytics_commands.py +442 -0
- attune/meta_workflows/cli_commands/config_commands.py +232 -0
- attune/meta_workflows/cli_commands/memory_commands.py +182 -0
- attune/meta_workflows/cli_commands/template_commands.py +354 -0
- attune/meta_workflows/cli_commands/workflow_commands.py +382 -0
- attune/meta_workflows/cli_meta_workflows.py +59 -0
- attune/meta_workflows/form_engine.py +292 -0
- attune/meta_workflows/intent_detector.py +409 -0
- attune/meta_workflows/models.py +569 -0
- attune/meta_workflows/pattern_learner.py +738 -0
- attune/meta_workflows/plan_generator.py +384 -0
- attune/meta_workflows/session_context.py +397 -0
- attune/meta_workflows/template_registry.py +229 -0
- attune/meta_workflows/workflow.py +984 -0
- attune/metrics/__init__.py +12 -0
- attune/metrics/collector.py +31 -0
- attune/metrics/prompt_metrics.py +194 -0
- attune/models/__init__.py +172 -0
- attune/models/__main__.py +13 -0
- attune/models/adaptive_routing.py +437 -0
- attune/models/auth_cli.py +444 -0
- attune/models/auth_strategy.py +450 -0
- attune/models/cli.py +655 -0
- attune/models/empathy_executor.py +354 -0
- attune/models/executor.py +257 -0
- attune/models/fallback.py +762 -0
- attune/models/provider_config.py +282 -0
- attune/models/registry.py +472 -0
- attune/models/tasks.py +359 -0
- attune/models/telemetry/__init__.py +71 -0
- attune/models/telemetry/analytics.py +594 -0
- attune/models/telemetry/backend.py +196 -0
- attune/models/telemetry/data_models.py +431 -0
- attune/models/telemetry/storage.py +489 -0
- attune/models/token_estimator.py +420 -0
- attune/models/validation.py +280 -0
- attune/monitoring/__init__.py +52 -0
- attune/monitoring/alerts.py +946 -0
- attune/monitoring/alerts_cli.py +448 -0
- attune/monitoring/multi_backend.py +271 -0
- attune/monitoring/otel_backend.py +362 -0
- attune/optimization/__init__.py +19 -0
- attune/optimization/context_optimizer.py +272 -0
- attune/orchestration/__init__.py +67 -0
- attune/orchestration/agent_templates.py +707 -0
- attune/orchestration/config_store.py +499 -0
- attune/orchestration/execution_strategies.py +2111 -0
- attune/orchestration/meta_orchestrator.py +1168 -0
- attune/orchestration/pattern_learner.py +696 -0
- attune/orchestration/real_tools.py +931 -0
- attune/pattern_cache.py +187 -0
- attune/pattern_library.py +542 -0
- attune/patterns/debugging/all_patterns.json +81 -0
- attune/patterns/debugging/workflow_20260107_1770825e.json +77 -0
- attune/patterns/refactoring_memory.json +89 -0
- attune/persistence.py +564 -0
- attune/platform_utils.py +265 -0
- attune/plugins/__init__.py +28 -0
- attune/plugins/base.py +361 -0
- attune/plugins/registry.py +268 -0
- attune/project_index/__init__.py +32 -0
- attune/project_index/cli.py +335 -0
- attune/project_index/index.py +667 -0
- attune/project_index/models.py +504 -0
- attune/project_index/reports.py +474 -0
- attune/project_index/scanner.py +777 -0
- attune/project_index/scanner_parallel.py +291 -0
- attune/prompts/__init__.py +61 -0
- attune/prompts/config.py +77 -0
- attune/prompts/context.py +177 -0
- attune/prompts/parser.py +285 -0
- attune/prompts/registry.py +313 -0
- attune/prompts/templates.py +208 -0
- attune/redis_config.py +302 -0
- attune/redis_memory.py +799 -0
- attune/resilience/__init__.py +56 -0
- attune/resilience/circuit_breaker.py +256 -0
- attune/resilience/fallback.py +179 -0
- attune/resilience/health.py +300 -0
- attune/resilience/retry.py +209 -0
- attune/resilience/timeout.py +135 -0
- attune/routing/__init__.py +43 -0
- attune/routing/chain_executor.py +433 -0
- attune/routing/classifier.py +217 -0
- attune/routing/smart_router.py +234 -0
- attune/routing/workflow_registry.py +343 -0
- attune/scaffolding/README.md +589 -0
- attune/scaffolding/__init__.py +35 -0
- attune/scaffolding/__main__.py +14 -0
- attune/scaffolding/cli.py +240 -0
- attune/scaffolding/templates/base_wizard.py.jinja2 +121 -0
- attune/scaffolding/templates/coach_wizard.py.jinja2 +321 -0
- attune/scaffolding/templates/domain_wizard.py.jinja2 +408 -0
- attune/scaffolding/templates/linear_flow_wizard.py.jinja2 +203 -0
- attune/socratic/__init__.py +256 -0
- attune/socratic/ab_testing.py +958 -0
- attune/socratic/blueprint.py +533 -0
- attune/socratic/cli.py +703 -0
- attune/socratic/collaboration.py +1114 -0
- attune/socratic/domain_templates.py +924 -0
- attune/socratic/embeddings.py +738 -0
- attune/socratic/engine.py +794 -0
- attune/socratic/explainer.py +682 -0
- attune/socratic/feedback.py +772 -0
- attune/socratic/forms.py +629 -0
- attune/socratic/generator.py +732 -0
- attune/socratic/llm_analyzer.py +637 -0
- attune/socratic/mcp_server.py +702 -0
- attune/socratic/session.py +312 -0
- attune/socratic/storage.py +667 -0
- attune/socratic/success.py +730 -0
- attune/socratic/visual_editor.py +860 -0
- attune/socratic/web_ui.py +958 -0
- attune/telemetry/__init__.py +39 -0
- attune/telemetry/agent_coordination.py +475 -0
- attune/telemetry/agent_tracking.py +367 -0
- attune/telemetry/approval_gates.py +545 -0
- attune/telemetry/cli.py +1231 -0
- attune/telemetry/commands/__init__.py +14 -0
- attune/telemetry/commands/dashboard_commands.py +696 -0
- attune/telemetry/event_streaming.py +409 -0
- attune/telemetry/feedback_loop.py +567 -0
- attune/telemetry/usage_tracker.py +591 -0
- attune/templates.py +754 -0
- attune/test_generator/__init__.py +38 -0
- attune/test_generator/__main__.py +14 -0
- attune/test_generator/cli.py +234 -0
- attune/test_generator/generator.py +355 -0
- attune/test_generator/risk_analyzer.py +216 -0
- attune/test_generator/templates/unit_test.py.jinja2 +272 -0
- attune/tier_recommender.py +384 -0
- attune/tools.py +183 -0
- attune/trust/__init__.py +28 -0
- attune/trust/circuit_breaker.py +579 -0
- attune/trust_building.py +527 -0
- attune/validation/__init__.py +19 -0
- attune/validation/xml_validator.py +281 -0
- attune/vscode_bridge.py +173 -0
- attune/workflow_commands.py +780 -0
- attune/workflow_patterns/__init__.py +33 -0
- attune/workflow_patterns/behavior.py +249 -0
- attune/workflow_patterns/core.py +76 -0
- attune/workflow_patterns/output.py +99 -0
- attune/workflow_patterns/registry.py +255 -0
- attune/workflow_patterns/structural.py +288 -0
- attune/workflows/__init__.py +539 -0
- attune/workflows/autonomous_test_gen.py +1268 -0
- attune/workflows/base.py +2667 -0
- attune/workflows/batch_processing.py +342 -0
- attune/workflows/bug_predict.py +1084 -0
- attune/workflows/builder.py +273 -0
- attune/workflows/caching.py +253 -0
- attune/workflows/code_review.py +1048 -0
- attune/workflows/code_review_adapters.py +312 -0
- attune/workflows/code_review_pipeline.py +722 -0
- attune/workflows/config.py +645 -0
- attune/workflows/dependency_check.py +644 -0
- attune/workflows/document_gen/__init__.py +25 -0
- attune/workflows/document_gen/config.py +30 -0
- attune/workflows/document_gen/report_formatter.py +162 -0
- attune/workflows/document_gen/workflow.py +1426 -0
- attune/workflows/document_manager.py +216 -0
- attune/workflows/document_manager_README.md +134 -0
- attune/workflows/documentation_orchestrator.py +1205 -0
- attune/workflows/history.py +510 -0
- attune/workflows/keyboard_shortcuts/__init__.py +39 -0
- attune/workflows/keyboard_shortcuts/generators.py +391 -0
- attune/workflows/keyboard_shortcuts/parsers.py +416 -0
- attune/workflows/keyboard_shortcuts/prompts.py +295 -0
- attune/workflows/keyboard_shortcuts/schema.py +193 -0
- attune/workflows/keyboard_shortcuts/workflow.py +509 -0
- attune/workflows/llm_base.py +363 -0
- attune/workflows/manage_docs.py +87 -0
- attune/workflows/manage_docs_README.md +134 -0
- attune/workflows/manage_documentation.py +821 -0
- attune/workflows/new_sample_workflow1.py +149 -0
- attune/workflows/new_sample_workflow1_README.md +150 -0
- attune/workflows/orchestrated_health_check.py +849 -0
- attune/workflows/orchestrated_release_prep.py +600 -0
- attune/workflows/output.py +413 -0
- attune/workflows/perf_audit.py +863 -0
- attune/workflows/pr_review.py +762 -0
- attune/workflows/progress.py +785 -0
- attune/workflows/progress_server.py +322 -0
- attune/workflows/progressive/README 2.md +454 -0
- attune/workflows/progressive/README.md +454 -0
- attune/workflows/progressive/__init__.py +82 -0
- attune/workflows/progressive/cli.py +219 -0
- attune/workflows/progressive/core.py +488 -0
- attune/workflows/progressive/orchestrator.py +723 -0
- attune/workflows/progressive/reports.py +520 -0
- attune/workflows/progressive/telemetry.py +274 -0
- attune/workflows/progressive/test_gen.py +495 -0
- attune/workflows/progressive/workflow.py +589 -0
- attune/workflows/refactor_plan.py +694 -0
- attune/workflows/release_prep.py +895 -0
- attune/workflows/release_prep_crew.py +969 -0
- attune/workflows/research_synthesis.py +404 -0
- attune/workflows/routing.py +168 -0
- attune/workflows/secure_release.py +593 -0
- attune/workflows/security_adapters.py +297 -0
- attune/workflows/security_audit.py +1329 -0
- attune/workflows/security_audit_phase3.py +355 -0
- attune/workflows/seo_optimization.py +633 -0
- attune/workflows/step_config.py +234 -0
- attune/workflows/telemetry_mixin.py +269 -0
- attune/workflows/test5.py +125 -0
- attune/workflows/test5_README.md +158 -0
- attune/workflows/test_coverage_boost_crew.py +849 -0
- attune/workflows/test_gen/__init__.py +52 -0
- attune/workflows/test_gen/ast_analyzer.py +249 -0
- attune/workflows/test_gen/config.py +88 -0
- attune/workflows/test_gen/data_models.py +38 -0
- attune/workflows/test_gen/report_formatter.py +289 -0
- attune/workflows/test_gen/test_templates.py +381 -0
- attune/workflows/test_gen/workflow.py +655 -0
- attune/workflows/test_gen.py +54 -0
- attune/workflows/test_gen_behavioral.py +477 -0
- attune/workflows/test_gen_parallel.py +341 -0
- attune/workflows/test_lifecycle.py +526 -0
- attune/workflows/test_maintenance.py +627 -0
- attune/workflows/test_maintenance_cli.py +590 -0
- attune/workflows/test_maintenance_crew.py +840 -0
- attune/workflows/test_runner.py +622 -0
- attune/workflows/tier_tracking.py +531 -0
- attune/workflows/xml_enhanced_crew.py +285 -0
- attune_ai-2.0.0.dist-info/METADATA +1026 -0
- attune_ai-2.0.0.dist-info/RECORD +457 -0
- attune_ai-2.0.0.dist-info/WHEEL +5 -0
- attune_ai-2.0.0.dist-info/entry_points.txt +26 -0
- attune_ai-2.0.0.dist-info/licenses/LICENSE +201 -0
- attune_ai-2.0.0.dist-info/licenses/LICENSE_CHANGE_ANNOUNCEMENT.md +101 -0
- attune_ai-2.0.0.dist-info/top_level.txt +5 -0
- attune_healthcare/__init__.py +13 -0
- attune_healthcare/monitors/__init__.py +9 -0
- attune_healthcare/monitors/clinical_protocol_monitor.py +315 -0
- attune_healthcare/monitors/monitoring/__init__.py +44 -0
- attune_healthcare/monitors/monitoring/protocol_checker.py +300 -0
- attune_healthcare/monitors/monitoring/protocol_loader.py +214 -0
- attune_healthcare/monitors/monitoring/sensor_parsers.py +306 -0
- attune_healthcare/monitors/monitoring/trajectory_analyzer.py +389 -0
- attune_llm/README.md +553 -0
- attune_llm/__init__.py +28 -0
- attune_llm/agent_factory/__init__.py +53 -0
- attune_llm/agent_factory/adapters/__init__.py +85 -0
- attune_llm/agent_factory/adapters/autogen_adapter.py +312 -0
- attune_llm/agent_factory/adapters/crewai_adapter.py +483 -0
- attune_llm/agent_factory/adapters/haystack_adapter.py +298 -0
- attune_llm/agent_factory/adapters/langchain_adapter.py +362 -0
- attune_llm/agent_factory/adapters/langgraph_adapter.py +333 -0
- attune_llm/agent_factory/adapters/native.py +228 -0
- attune_llm/agent_factory/adapters/wizard_adapter.py +423 -0
- attune_llm/agent_factory/base.py +305 -0
- attune_llm/agent_factory/crews/__init__.py +67 -0
- attune_llm/agent_factory/crews/code_review.py +1113 -0
- attune_llm/agent_factory/crews/health_check.py +1262 -0
- attune_llm/agent_factory/crews/refactoring.py +1128 -0
- attune_llm/agent_factory/crews/security_audit.py +1018 -0
- attune_llm/agent_factory/decorators.py +287 -0
- attune_llm/agent_factory/factory.py +558 -0
- attune_llm/agent_factory/framework.py +193 -0
- attune_llm/agent_factory/memory_integration.py +328 -0
- attune_llm/agent_factory/resilient.py +320 -0
- attune_llm/agents_md/__init__.py +22 -0
- attune_llm/agents_md/loader.py +218 -0
- attune_llm/agents_md/parser.py +271 -0
- attune_llm/agents_md/registry.py +307 -0
- attune_llm/claude_memory.py +466 -0
- attune_llm/cli/__init__.py +8 -0
- attune_llm/cli/sync_claude.py +487 -0
- attune_llm/code_health.py +1313 -0
- attune_llm/commands/__init__.py +51 -0
- attune_llm/commands/context.py +375 -0
- attune_llm/commands/loader.py +301 -0
- attune_llm/commands/models.py +231 -0
- attune_llm/commands/parser.py +371 -0
- attune_llm/commands/registry.py +429 -0
- attune_llm/config/__init__.py +29 -0
- attune_llm/config/unified.py +291 -0
- attune_llm/context/__init__.py +22 -0
- attune_llm/context/compaction.py +455 -0
- attune_llm/context/manager.py +434 -0
- attune_llm/contextual_patterns.py +361 -0
- attune_llm/core.py +907 -0
- attune_llm/git_pattern_extractor.py +435 -0
- attune_llm/hooks/__init__.py +24 -0
- attune_llm/hooks/config.py +306 -0
- attune_llm/hooks/executor.py +289 -0
- attune_llm/hooks/registry.py +302 -0
- attune_llm/hooks/scripts/__init__.py +39 -0
- attune_llm/hooks/scripts/evaluate_session.py +201 -0
- attune_llm/hooks/scripts/first_time_init.py +285 -0
- attune_llm/hooks/scripts/pre_compact.py +207 -0
- attune_llm/hooks/scripts/session_end.py +183 -0
- attune_llm/hooks/scripts/session_start.py +163 -0
- attune_llm/hooks/scripts/suggest_compact.py +225 -0
- attune_llm/learning/__init__.py +30 -0
- attune_llm/learning/evaluator.py +438 -0
- attune_llm/learning/extractor.py +514 -0
- attune_llm/learning/storage.py +560 -0
- attune_llm/levels.py +227 -0
- attune_llm/pattern_confidence.py +414 -0
- attune_llm/pattern_resolver.py +272 -0
- attune_llm/pattern_summary.py +350 -0
- attune_llm/providers.py +967 -0
- attune_llm/routing/__init__.py +32 -0
- attune_llm/routing/model_router.py +362 -0
- attune_llm/security/IMPLEMENTATION_SUMMARY.md +413 -0
- attune_llm/security/PHASE2_COMPLETE.md +384 -0
- attune_llm/security/PHASE2_SECRETS_DETECTOR_COMPLETE.md +271 -0
- attune_llm/security/QUICK_REFERENCE.md +316 -0
- attune_llm/security/README.md +262 -0
- attune_llm/security/__init__.py +62 -0
- attune_llm/security/audit_logger.py +929 -0
- attune_llm/security/audit_logger_example.py +152 -0
- attune_llm/security/pii_scrubber.py +640 -0
- attune_llm/security/secrets_detector.py +678 -0
- attune_llm/security/secrets_detector_example.py +304 -0
- attune_llm/security/secure_memdocs.py +1192 -0
- attune_llm/security/secure_memdocs_example.py +278 -0
- attune_llm/session_status.py +745 -0
- attune_llm/state.py +246 -0
- attune_llm/utils/__init__.py +5 -0
- attune_llm/utils/tokens.py +349 -0
- attune_software/SOFTWARE_PLUGIN_README.md +57 -0
- attune_software/__init__.py +13 -0
- attune_software/cli/__init__.py +120 -0
- attune_software/cli/inspect.py +362 -0
- attune_software/cli.py +574 -0
- attune_software/plugin.py +188 -0
- workflow_scaffolding/__init__.py +11 -0
- workflow_scaffolding/__main__.py +12 -0
- workflow_scaffolding/cli.py +206 -0
- workflow_scaffolding/generator.py +265 -0
|
@@ -0,0 +1,1113 @@
|
|
|
1
|
+
"""Code Review Crew
|
|
2
|
+
|
|
3
|
+
A multi-agent crew that performs comprehensive code reviews.
|
|
4
|
+
Demonstrates CrewAI's hierarchical collaboration patterns with:
|
|
5
|
+
- 5 specialized agents with distinct roles
|
|
6
|
+
- Hierarchical task delegation from Review Lead
|
|
7
|
+
- Memory Graph integration for cross-review learning
|
|
8
|
+
- Structured output with verdict and recommendations
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
from attune_llm.agent_factory.crews import CodeReviewCrew
|
|
12
|
+
|
|
13
|
+
crew = CodeReviewCrew(api_key="...")
|
|
14
|
+
report = await crew.review(diff="...", files_changed=["src/api.py"])
|
|
15
|
+
|
|
16
|
+
print(f"Verdict: {report.verdict}")
|
|
17
|
+
for finding in report.critical_findings:
|
|
18
|
+
print(f" - {finding.title}: {finding.suggestion}")
|
|
19
|
+
|
|
20
|
+
Copyright 2025 Smart-AI-Memory
|
|
21
|
+
Licensed under Fair Source License 0.9
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
import logging
|
|
25
|
+
from dataclasses import dataclass, field
|
|
26
|
+
from enum import Enum
|
|
27
|
+
from typing import Any
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger(__name__)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Severity(Enum):
|
|
33
|
+
"""Review finding severity levels."""
|
|
34
|
+
|
|
35
|
+
CRITICAL = "critical"
|
|
36
|
+
HIGH = "high"
|
|
37
|
+
MEDIUM = "medium"
|
|
38
|
+
LOW = "low"
|
|
39
|
+
INFO = "info"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class FindingCategory(Enum):
|
|
43
|
+
"""Code review finding categories."""
|
|
44
|
+
|
|
45
|
+
SECURITY = "security"
|
|
46
|
+
ARCHITECTURE = "architecture"
|
|
47
|
+
QUALITY = "quality"
|
|
48
|
+
PERFORMANCE = "performance"
|
|
49
|
+
MAINTAINABILITY = "maintainability"
|
|
50
|
+
TESTING = "testing"
|
|
51
|
+
DOCUMENTATION = "documentation"
|
|
52
|
+
STYLE = "style"
|
|
53
|
+
BUG = "bug"
|
|
54
|
+
OTHER = "other"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class Verdict(Enum):
|
|
58
|
+
"""Code review verdict."""
|
|
59
|
+
|
|
60
|
+
APPROVE = "approve"
|
|
61
|
+
APPROVE_WITH_SUGGESTIONS = "approve_with_suggestions"
|
|
62
|
+
REQUEST_CHANGES = "request_changes"
|
|
63
|
+
REJECT = "reject"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass
|
|
67
|
+
class ReviewFinding:
|
|
68
|
+
"""A single finding from the code review."""
|
|
69
|
+
|
|
70
|
+
title: str
|
|
71
|
+
description: str
|
|
72
|
+
severity: Severity
|
|
73
|
+
category: FindingCategory
|
|
74
|
+
file_path: str | None = None
|
|
75
|
+
line_number: int | None = None
|
|
76
|
+
code_snippet: str | None = None
|
|
77
|
+
suggestion: str | None = None
|
|
78
|
+
before_code: str | None = None
|
|
79
|
+
after_code: str | None = None
|
|
80
|
+
confidence: float = 1.0
|
|
81
|
+
metadata: dict = field(default_factory=dict)
|
|
82
|
+
|
|
83
|
+
def to_dict(self) -> dict:
|
|
84
|
+
"""Convert finding to dictionary."""
|
|
85
|
+
return {
|
|
86
|
+
"title": self.title,
|
|
87
|
+
"description": self.description,
|
|
88
|
+
"severity": self.severity.value,
|
|
89
|
+
"category": self.category.value,
|
|
90
|
+
"file_path": self.file_path,
|
|
91
|
+
"line_number": self.line_number,
|
|
92
|
+
"code_snippet": self.code_snippet,
|
|
93
|
+
"suggestion": self.suggestion,
|
|
94
|
+
"before_code": self.before_code,
|
|
95
|
+
"after_code": self.after_code,
|
|
96
|
+
"confidence": self.confidence,
|
|
97
|
+
"metadata": self.metadata,
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@dataclass
|
|
102
|
+
class CodeReviewReport:
|
|
103
|
+
"""Complete code review report."""
|
|
104
|
+
|
|
105
|
+
target: str
|
|
106
|
+
findings: list[ReviewFinding]
|
|
107
|
+
verdict: Verdict
|
|
108
|
+
summary: str = ""
|
|
109
|
+
review_duration_seconds: float = 0.0
|
|
110
|
+
agents_used: list[str] = field(default_factory=list)
|
|
111
|
+
memory_graph_hits: int = 0
|
|
112
|
+
metadata: dict = field(default_factory=dict)
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def critical_findings(self) -> list[ReviewFinding]:
|
|
116
|
+
"""Get critical severity findings."""
|
|
117
|
+
return [f for f in self.findings if f.severity == Severity.CRITICAL]
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def high_findings(self) -> list[ReviewFinding]:
|
|
121
|
+
"""Get high severity findings."""
|
|
122
|
+
return [f for f in self.findings if f.severity == Severity.HIGH]
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def findings_by_category(self) -> dict[str, list[ReviewFinding]]:
|
|
126
|
+
"""Group findings by category."""
|
|
127
|
+
result: dict[str, list[ReviewFinding]] = {}
|
|
128
|
+
for finding in self.findings:
|
|
129
|
+
cat = finding.category.value
|
|
130
|
+
if cat not in result:
|
|
131
|
+
result[cat] = []
|
|
132
|
+
result[cat].append(finding)
|
|
133
|
+
return result
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def quality_score(self) -> float:
|
|
137
|
+
"""Calculate overall quality score (0-100, higher is better)."""
|
|
138
|
+
if not self.findings:
|
|
139
|
+
return 100.0
|
|
140
|
+
|
|
141
|
+
# Start with 100 and deduct based on severity
|
|
142
|
+
deductions = {
|
|
143
|
+
Severity.CRITICAL: 25,
|
|
144
|
+
Severity.HIGH: 15,
|
|
145
|
+
Severity.MEDIUM: 5,
|
|
146
|
+
Severity.LOW: 2,
|
|
147
|
+
Severity.INFO: 0.5,
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
total_deduction = sum(deductions[f.severity] * f.confidence for f in self.findings)
|
|
151
|
+
return max(0.0, 100.0 - total_deduction)
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def has_blocking_issues(self) -> bool:
|
|
155
|
+
"""Check if there are issues that should block merge."""
|
|
156
|
+
return len(self.critical_findings) > 0 or len(self.high_findings) > 3
|
|
157
|
+
|
|
158
|
+
def to_dict(self) -> dict:
|
|
159
|
+
"""Convert report to dictionary."""
|
|
160
|
+
return {
|
|
161
|
+
"target": self.target,
|
|
162
|
+
"findings": [f.to_dict() for f in self.findings],
|
|
163
|
+
"verdict": self.verdict.value,
|
|
164
|
+
"summary": self.summary,
|
|
165
|
+
"review_duration_seconds": self.review_duration_seconds,
|
|
166
|
+
"agents_used": self.agents_used,
|
|
167
|
+
"memory_graph_hits": self.memory_graph_hits,
|
|
168
|
+
"quality_score": self.quality_score,
|
|
169
|
+
"has_blocking_issues": self.has_blocking_issues,
|
|
170
|
+
"finding_counts": {
|
|
171
|
+
"critical": len(self.critical_findings),
|
|
172
|
+
"high": len(self.high_findings),
|
|
173
|
+
"total": len(self.findings),
|
|
174
|
+
},
|
|
175
|
+
"metadata": self.metadata,
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@dataclass
|
|
180
|
+
class CodeReviewConfig:
|
|
181
|
+
"""Configuration for code review crew."""
|
|
182
|
+
|
|
183
|
+
# API Configuration
|
|
184
|
+
provider: str = "anthropic"
|
|
185
|
+
api_key: str | None = None
|
|
186
|
+
|
|
187
|
+
# Review Configuration
|
|
188
|
+
review_depth: str = "standard" # "quick", "standard", "thorough"
|
|
189
|
+
focus_areas: list[str] = field(
|
|
190
|
+
default_factory=lambda: ["security", "architecture", "quality", "performance"],
|
|
191
|
+
)
|
|
192
|
+
check_tests: bool = True
|
|
193
|
+
check_docs: bool = False
|
|
194
|
+
|
|
195
|
+
# Memory Graph
|
|
196
|
+
memory_graph_enabled: bool = True
|
|
197
|
+
memory_graph_path: str = "patterns/code_review_memory.json"
|
|
198
|
+
|
|
199
|
+
# Agent Tiers
|
|
200
|
+
lead_tier: str = "premium"
|
|
201
|
+
security_tier: str = "capable"
|
|
202
|
+
architecture_tier: str = "premium"
|
|
203
|
+
quality_tier: str = "capable"
|
|
204
|
+
performance_tier: str = "capable"
|
|
205
|
+
|
|
206
|
+
# Resilience
|
|
207
|
+
resilience_enabled: bool = True
|
|
208
|
+
timeout_seconds: float = 300.0
|
|
209
|
+
|
|
210
|
+
# XML Prompts
|
|
211
|
+
xml_prompts_enabled: bool = True
|
|
212
|
+
xml_schema_version: str = "1.0"
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
# XML Prompt Templates for Code Review Agents
|
|
216
|
+
XML_PROMPT_TEMPLATES = {
|
|
217
|
+
"review_lead": """<agent role="review_lead" version="{schema_version}">
|
|
218
|
+
<identity>
|
|
219
|
+
<role>Code Review Lead</role>
|
|
220
|
+
<expertise>Code review coordination, technical leadership, quality assessment</expertise>
|
|
221
|
+
</identity>
|
|
222
|
+
|
|
223
|
+
<goal>
|
|
224
|
+
Coordinate the code review team to provide comprehensive, actionable feedback.
|
|
225
|
+
Synthesize findings from all reviewers into a clear verdict and summary.
|
|
226
|
+
</goal>
|
|
227
|
+
|
|
228
|
+
<instructions>
|
|
229
|
+
<step>Coordinate the code review team and delegate to specialists</step>
|
|
230
|
+
<step>Synthesize findings from Security, Architecture, Quality, and Performance reviewers</step>
|
|
231
|
+
<step>Prioritize issues by severity and impact</step>
|
|
232
|
+
<step>Make final verdict (APPROVE, APPROVE_WITH_SUGGESTIONS, REQUEST_CHANGES, REJECT)</step>
|
|
233
|
+
<step>Generate actionable summary with specific next steps</step>
|
|
234
|
+
</instructions>
|
|
235
|
+
|
|
236
|
+
<constraints>
|
|
237
|
+
<rule>Be constructive and specific in all feedback</rule>
|
|
238
|
+
<rule>Prioritize blocking issues over style preferences</rule>
|
|
239
|
+
<rule>Provide code examples for complex suggestions</rule>
|
|
240
|
+
<rule>Consider the reviewer's context and time constraints</rule>
|
|
241
|
+
</constraints>
|
|
242
|
+
|
|
243
|
+
<verdict_criteria>
|
|
244
|
+
<option name="APPROVE">No issues or only minor suggestions that don't require changes</option>
|
|
245
|
+
<option name="APPROVE_WITH_SUGGESTIONS">Good overall, non-blocking improvements</option>
|
|
246
|
+
<option name="REQUEST_CHANGES">Issues that must be addressed before merge</option>
|
|
247
|
+
<option name="REJECT">Fundamental problems requiring significant rework</option>
|
|
248
|
+
</verdict_criteria>
|
|
249
|
+
|
|
250
|
+
<output_format>
|
|
251
|
+
<section name="summary">Executive summary of review findings</section>
|
|
252
|
+
<section name="verdict">Final verdict with confidence level</section>
|
|
253
|
+
<section name="findings">Prioritized list of issues by severity</section>
|
|
254
|
+
<section name="checklist">Action items for the author</section>
|
|
255
|
+
</output_format>
|
|
256
|
+
</agent>""",
|
|
257
|
+
"security_analyst": """<agent role="security_analyst" version="{schema_version}">
|
|
258
|
+
<identity>
|
|
259
|
+
<role>Security Analyst</role>
|
|
260
|
+
<expertise>Application security, OWASP Top 10, secure coding practices</expertise>
|
|
261
|
+
</identity>
|
|
262
|
+
|
|
263
|
+
<goal>
|
|
264
|
+
Identify security vulnerabilities and provide actionable remediation guidance.
|
|
265
|
+
</goal>
|
|
266
|
+
|
|
267
|
+
<instructions>
|
|
268
|
+
<step>Scan for OWASP Top 10 vulnerabilities</step>
|
|
269
|
+
<step>Check for hardcoded secrets, API keys, and credentials</step>
|
|
270
|
+
<step>Review authentication and authorization logic</step>
|
|
271
|
+
<step>Assess input validation and output encoding</step>
|
|
272
|
+
<step>Identify insecure dependencies</step>
|
|
273
|
+
<step>Provide specific remediation with code examples</step>
|
|
274
|
+
</instructions>
|
|
275
|
+
|
|
276
|
+
<constraints>
|
|
277
|
+
<rule>Minimize false positives - focus on exploitable issues</rule>
|
|
278
|
+
<rule>Include file path and line number for each finding</rule>
|
|
279
|
+
<rule>Rate severity as critical/high/medium/low</rule>
|
|
280
|
+
<rule>Provide proof-of-concept or attack scenario where applicable</rule>
|
|
281
|
+
</constraints>
|
|
282
|
+
|
|
283
|
+
<vulnerability_categories>
|
|
284
|
+
<category>SQL Injection</category>
|
|
285
|
+
<category>Cross-Site Scripting (XSS)</category>
|
|
286
|
+
<category>Command Injection</category>
|
|
287
|
+
<category>Path Traversal</category>
|
|
288
|
+
<category>Authentication Bypass</category>
|
|
289
|
+
<category>Insecure Deserialization</category>
|
|
290
|
+
<category>Sensitive Data Exposure</category>
|
|
291
|
+
</vulnerability_categories>
|
|
292
|
+
|
|
293
|
+
<output_format>
|
|
294
|
+
<section name="findings">Vulnerabilities with severity, location, and remediation</section>
|
|
295
|
+
<section name="summary">Overall security posture assessment</section>
|
|
296
|
+
</output_format>
|
|
297
|
+
</agent>""",
|
|
298
|
+
"architecture_reviewer": """<agent role="architecture_reviewer" version="{schema_version}">
|
|
299
|
+
<identity>
|
|
300
|
+
<role>Architecture Reviewer</role>
|
|
301
|
+
<expertise>Software architecture, design patterns, SOLID principles</expertise>
|
|
302
|
+
</identity>
|
|
303
|
+
|
|
304
|
+
<goal>
|
|
305
|
+
Evaluate code architecture and design, ensuring maintainability and scalability.
|
|
306
|
+
</goal>
|
|
307
|
+
|
|
308
|
+
<instructions>
|
|
309
|
+
<step>Evaluate adherence to SOLID principles</step>
|
|
310
|
+
<step>Identify design pattern usage and anti-patterns</step>
|
|
311
|
+
<step>Assess module boundaries and coupling</step>
|
|
312
|
+
<step>Review dependency direction and layering</step>
|
|
313
|
+
<step>Consider scalability and extensibility</step>
|
|
314
|
+
<step>Provide refactoring suggestions with before/after examples</step>
|
|
315
|
+
</instructions>
|
|
316
|
+
|
|
317
|
+
<constraints>
|
|
318
|
+
<rule>Consider the project's architectural context</rule>
|
|
319
|
+
<rule>Balance ideal architecture with pragmatic solutions</rule>
|
|
320
|
+
<rule>Provide concrete refactoring steps</rule>
|
|
321
|
+
<rule>Highlight breaking changes that affect other modules</rule>
|
|
322
|
+
</constraints>
|
|
323
|
+
|
|
324
|
+
<principles>
|
|
325
|
+
<principle name="SRP">Single Responsibility - one reason to change</principle>
|
|
326
|
+
<principle name="OCP">Open/Closed - open for extension, closed for modification</principle>
|
|
327
|
+
<principle name="LSP">Liskov Substitution - subtypes must be substitutable</principle>
|
|
328
|
+
<principle name="ISP">Interface Segregation - prefer small, focused interfaces</principle>
|
|
329
|
+
<principle name="DIP">Dependency Inversion - depend on abstractions</principle>
|
|
330
|
+
</principles>
|
|
331
|
+
|
|
332
|
+
<output_format>
|
|
333
|
+
<section name="findings">Architecture issues with impact and suggestions</section>
|
|
334
|
+
<section name="summary">Overall design assessment</section>
|
|
335
|
+
</output_format>
|
|
336
|
+
</agent>""",
|
|
337
|
+
"quality_analyst": """<agent role="quality_analyst" version="{schema_version}">
|
|
338
|
+
<identity>
|
|
339
|
+
<role>Quality Analyst</role>
|
|
340
|
+
<expertise>Code quality, maintainability, testing, code smells</expertise>
|
|
341
|
+
</identity>
|
|
342
|
+
|
|
343
|
+
<goal>
|
|
344
|
+
Identify code quality issues that affect long-term maintainability.
|
|
345
|
+
</goal>
|
|
346
|
+
|
|
347
|
+
<instructions>
|
|
348
|
+
<step>Identify code smells (long methods, large classes, duplication)</step>
|
|
349
|
+
<step>Assess naming clarity and code readability</step>
|
|
350
|
+
<step>Review error handling and logging</step>
|
|
351
|
+
<step>Check test coverage and test quality</step>
|
|
352
|
+
<step>Evaluate complexity (cyclomatic, cognitive)</step>
|
|
353
|
+
<step>Prioritize issues by maintainability impact</step>
|
|
354
|
+
</instructions>
|
|
355
|
+
|
|
356
|
+
<constraints>
|
|
357
|
+
<rule>Focus on issues that affect long-term maintenance</rule>
|
|
358
|
+
<rule>Distinguish between style preferences and real problems</rule>
|
|
359
|
+
<rule>Consider the team's coding standards</rule>
|
|
360
|
+
<rule>Provide actionable improvement suggestions</rule>
|
|
361
|
+
</constraints>
|
|
362
|
+
|
|
363
|
+
<code_smells>
|
|
364
|
+
<smell>Long Method - methods over 20-30 lines</smell>
|
|
365
|
+
<smell>Large Class - classes with too many responsibilities</smell>
|
|
366
|
+
<smell>Duplicate Code - copy-pasted logic</smell>
|
|
367
|
+
<smell>Dead Code - unused variables, functions, imports</smell>
|
|
368
|
+
<smell>Magic Numbers - unexplained literal values</smell>
|
|
369
|
+
<smell>Deep Nesting - excessive indentation levels</smell>
|
|
370
|
+
</code_smells>
|
|
371
|
+
|
|
372
|
+
<output_format>
|
|
373
|
+
<section name="findings">Quality issues with severity and suggestions</section>
|
|
374
|
+
<section name="summary">Overall code quality assessment</section>
|
|
375
|
+
</output_format>
|
|
376
|
+
</agent>""",
|
|
377
|
+
"performance_reviewer": """<agent role="performance_reviewer" version="{schema_version}">
|
|
378
|
+
<identity>
|
|
379
|
+
<role>Performance Reviewer</role>
|
|
380
|
+
<expertise>Performance optimization, algorithm efficiency, resource management</expertise>
|
|
381
|
+
</identity>
|
|
382
|
+
|
|
383
|
+
<goal>
|
|
384
|
+
Identify performance issues and suggest optimizations with expected impact.
|
|
385
|
+
</goal>
|
|
386
|
+
|
|
387
|
+
<instructions>
|
|
388
|
+
<step>Analyze algorithm time and space complexity</step>
|
|
389
|
+
<step>Identify inefficient data structures or operations</step>
|
|
390
|
+
<step>Check for resource leaks (memory, connections, handles)</step>
|
|
391
|
+
<step>Review database query patterns (N+1, missing indexes)</step>
|
|
392
|
+
<step>Identify blocking operations in async code</step>
|
|
393
|
+
<step>Provide optimization suggestions with expected impact</step>
|
|
394
|
+
</instructions>
|
|
395
|
+
|
|
396
|
+
<constraints>
|
|
397
|
+
<rule>Focus on measurable performance impact</rule>
|
|
398
|
+
<rule>Consider trade-offs (readability vs performance)</rule>
|
|
399
|
+
<rule>Prioritize by frequency of execution</rule>
|
|
400
|
+
<rule>Suggest profiling for uncertain impacts</rule>
|
|
401
|
+
</constraints>
|
|
402
|
+
|
|
403
|
+
<anti_patterns>
|
|
404
|
+
<pattern>N+1 Queries - separate query per item in collection</pattern>
|
|
405
|
+
<pattern>Sync in Async - blocking calls in async code</pattern>
|
|
406
|
+
<pattern>String Concatenation in Loop - O(n²) string building</pattern>
|
|
407
|
+
<pattern>Unoptimized Regex - catastrophic backtracking</pattern>
|
|
408
|
+
<pattern>Memory Leaks - unreleased resources</pattern>
|
|
409
|
+
<pattern>Over-fetching - retrieving more data than needed</pattern>
|
|
410
|
+
</anti_patterns>
|
|
411
|
+
|
|
412
|
+
<output_format>
|
|
413
|
+
<section name="findings">Performance issues with impact and optimizations</section>
|
|
414
|
+
<section name="summary">Overall performance assessment</section>
|
|
415
|
+
</output_format>
|
|
416
|
+
</agent>""",
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
class CodeReviewCrew:
|
|
421
|
+
"""Multi-agent crew for comprehensive code reviews.
|
|
422
|
+
|
|
423
|
+
The crew consists of 5 specialized agents:
|
|
424
|
+
|
|
425
|
+
1. **Review Lead** (Coordinator)
|
|
426
|
+
- Orchestrates the review team
|
|
427
|
+
- Synthesizes findings from all agents
|
|
428
|
+
- Makes final verdict decision
|
|
429
|
+
- Generates executive summary
|
|
430
|
+
- Model: Premium tier
|
|
431
|
+
|
|
432
|
+
2. **Security Analyst** (Security Expert)
|
|
433
|
+
- Reviews for security vulnerabilities
|
|
434
|
+
- OWASP Top 10 focus
|
|
435
|
+
- Checks for hardcoded secrets
|
|
436
|
+
- Model: Capable tier
|
|
437
|
+
|
|
438
|
+
3. **Architecture Reviewer** (Architect)
|
|
439
|
+
- Evaluates design patterns
|
|
440
|
+
- Checks SOLID principles
|
|
441
|
+
- Assesses coupling and cohesion
|
|
442
|
+
- Model: Premium tier
|
|
443
|
+
|
|
444
|
+
4. **Quality Analyst** (Quality Engineer)
|
|
445
|
+
- Identifies code smells
|
|
446
|
+
- Checks maintainability
|
|
447
|
+
- Reviews test coverage
|
|
448
|
+
- Model: Capable tier
|
|
449
|
+
|
|
450
|
+
5. **Performance Reviewer** (Performance Engineer)
|
|
451
|
+
- Identifies performance issues
|
|
452
|
+
- Suggests optimizations
|
|
453
|
+
- Checks for anti-patterns
|
|
454
|
+
- Model: Capable tier
|
|
455
|
+
|
|
456
|
+
Example:
|
|
457
|
+
crew = CodeReviewCrew(api_key="...")
|
|
458
|
+
report = await crew.review(
|
|
459
|
+
diff="...",
|
|
460
|
+
files_changed=["src/api.py"],
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
# Access verdict
|
|
464
|
+
if report.verdict == Verdict.APPROVE:
|
|
465
|
+
print("Code is ready to merge!")
|
|
466
|
+
|
|
467
|
+
# Get quality score
|
|
468
|
+
print(f"Quality Score: {report.quality_score}/100")
|
|
469
|
+
|
|
470
|
+
"""
|
|
471
|
+
|
|
472
|
+
def __init__(self, config: CodeReviewConfig | None = None, **kwargs: Any):
|
|
473
|
+
"""Initialize the Code Review Crew.
|
|
474
|
+
|
|
475
|
+
Args:
|
|
476
|
+
config: CodeReviewConfig or pass individual params as kwargs
|
|
477
|
+
**kwargs: Individual config parameters (api_key, provider, etc.)
|
|
478
|
+
|
|
479
|
+
"""
|
|
480
|
+
if config:
|
|
481
|
+
self.config = config
|
|
482
|
+
else:
|
|
483
|
+
self.config = CodeReviewConfig(**kwargs)
|
|
484
|
+
|
|
485
|
+
self._factory: Any = None
|
|
486
|
+
self._agents: dict[str, Any] = {}
|
|
487
|
+
self._workflow: Any = None
|
|
488
|
+
self._graph: Any = None
|
|
489
|
+
self._initialized = False
|
|
490
|
+
|
|
491
|
+
def _render_xml_prompt(self, template_key: str) -> str:
|
|
492
|
+
"""Render XML prompt template with config values."""
|
|
493
|
+
template = XML_PROMPT_TEMPLATES.get(template_key, "")
|
|
494
|
+
return template.format(schema_version=self.config.xml_schema_version)
|
|
495
|
+
|
|
496
|
+
def _get_system_prompt(self, agent_key: str, fallback: str) -> str:
|
|
497
|
+
"""Get system prompt - XML if enabled, fallback otherwise."""
|
|
498
|
+
if self.config.xml_prompts_enabled:
|
|
499
|
+
return self._render_xml_prompt(agent_key)
|
|
500
|
+
return fallback
|
|
501
|
+
|
|
502
|
+
async def _initialize(self) -> None:
|
|
503
|
+
"""Lazy initialization of agents and workflow."""
|
|
504
|
+
if self._initialized:
|
|
505
|
+
return
|
|
506
|
+
|
|
507
|
+
from attune_llm.agent_factory import AgentFactory, Framework
|
|
508
|
+
|
|
509
|
+
# Check if CrewAI is available
|
|
510
|
+
try:
|
|
511
|
+
from attune_llm.agent_factory.adapters.crewai_adapter import _check_crewai
|
|
512
|
+
|
|
513
|
+
use_crewai = _check_crewai()
|
|
514
|
+
except ImportError:
|
|
515
|
+
use_crewai = False
|
|
516
|
+
|
|
517
|
+
# Use CrewAI if available, otherwise fall back to Native
|
|
518
|
+
framework = Framework.CREWAI if use_crewai else Framework.NATIVE
|
|
519
|
+
|
|
520
|
+
self._factory = AgentFactory(
|
|
521
|
+
framework=framework,
|
|
522
|
+
provider=self.config.provider,
|
|
523
|
+
api_key=self.config.api_key,
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
# Initialize Memory Graph if enabled
|
|
527
|
+
if self.config.memory_graph_enabled:
|
|
528
|
+
try:
|
|
529
|
+
from attune.memory import MemoryGraph
|
|
530
|
+
|
|
531
|
+
self._graph = MemoryGraph(path=self.config.memory_graph_path)
|
|
532
|
+
except ImportError:
|
|
533
|
+
logger.warning("Memory Graph not available, continuing without it")
|
|
534
|
+
|
|
535
|
+
# Create the 5 specialized agents
|
|
536
|
+
await self._create_agents()
|
|
537
|
+
|
|
538
|
+
# Create hierarchical workflow
|
|
539
|
+
await self._create_workflow()
|
|
540
|
+
|
|
541
|
+
self._initialized = True
|
|
542
|
+
|
|
543
|
+
async def _create_agents(self) -> None:
|
|
544
|
+
"""Create the 5 specialized code review agents with XML-enhanced prompts."""
|
|
545
|
+
# Fallback prompts for when XML is disabled
|
|
546
|
+
lead_fallback = """You are the Review Lead, a senior engineer with 15+ years.
|
|
547
|
+
|
|
548
|
+
Your responsibilities:
|
|
549
|
+
1. Coordinate the code review team
|
|
550
|
+
2. Synthesize findings from all reviewers
|
|
551
|
+
3. Prioritize issues by impact
|
|
552
|
+
4. Make final verdict decision (approve, request_changes, reject)
|
|
553
|
+
5. Generate actionable summary
|
|
554
|
+
|
|
555
|
+
You delegate to your team:
|
|
556
|
+
- Security Analyst: Security vulnerabilities and risks
|
|
557
|
+
- Architecture Reviewer: Design patterns and structure
|
|
558
|
+
- Quality Analyst: Code quality and maintainability
|
|
559
|
+
- Performance Reviewer: Performance issues and optimizations
|
|
560
|
+
|
|
561
|
+
For verdict decisions:
|
|
562
|
+
- APPROVE: No issues or only minor suggestions
|
|
563
|
+
- APPROVE_WITH_SUGGESTIONS: Good overall, some improvements recommended
|
|
564
|
+
- REQUEST_CHANGES: Issues that must be addressed before merge
|
|
565
|
+
- REJECT: Fundamental problems requiring significant rework
|
|
566
|
+
|
|
567
|
+
Be constructive and specific in feedback."""
|
|
568
|
+
|
|
569
|
+
# 1. Review Lead (Coordinator)
|
|
570
|
+
self._agents["lead"] = self._factory.create_agent(
|
|
571
|
+
name="review_lead",
|
|
572
|
+
role="coordinator",
|
|
573
|
+
description="Senior engineer who orchestrates the code review team",
|
|
574
|
+
system_prompt=self._get_system_prompt("review_lead", lead_fallback),
|
|
575
|
+
model_tier=self.config.lead_tier,
|
|
576
|
+
memory_graph_enabled=self.config.memory_graph_enabled,
|
|
577
|
+
memory_graph_path=self.config.memory_graph_path,
|
|
578
|
+
resilience_enabled=self.config.resilience_enabled,
|
|
579
|
+
)
|
|
580
|
+
|
|
581
|
+
# Fallback prompts for remaining agents
|
|
582
|
+
security_fallback = """You are the Security Analyst, a security-focused reviewer.
|
|
583
|
+
|
|
584
|
+
Your focus areas:
|
|
585
|
+
1. OWASP Top 10 vulnerabilities
|
|
586
|
+
2. SQL Injection, XSS, Command Injection
|
|
587
|
+
3. Hardcoded secrets, API keys, passwords
|
|
588
|
+
4. Authentication and authorization flaws
|
|
589
|
+
5. Input validation issues
|
|
590
|
+
6. Insecure dependencies
|
|
591
|
+
7. Cryptographic weaknesses
|
|
592
|
+
|
|
593
|
+
For each finding, provide:
|
|
594
|
+
- Clear description of the security risk
|
|
595
|
+
- File and line number
|
|
596
|
+
- Severity (critical/high/medium/low)
|
|
597
|
+
- Specific remediation with code example
|
|
598
|
+
|
|
599
|
+
Be thorough but minimize false positives. Focus on exploitable issues."""
|
|
600
|
+
|
|
601
|
+
architecture_fallback = """You are the Architecture Reviewer, a software architect.
|
|
602
|
+
|
|
603
|
+
Your evaluation criteria:
|
|
604
|
+
1. SOLID Principles
|
|
605
|
+
- Single Responsibility
|
|
606
|
+
- Open/Closed
|
|
607
|
+
- Liskov Substitution
|
|
608
|
+
- Interface Segregation
|
|
609
|
+
- Dependency Inversion
|
|
610
|
+
|
|
611
|
+
2. Design Patterns
|
|
612
|
+
- Appropriate pattern usage
|
|
613
|
+
- Anti-patterns to avoid
|
|
614
|
+
- Missing patterns where beneficial
|
|
615
|
+
|
|
616
|
+
3. Code Structure
|
|
617
|
+
- Module boundaries
|
|
618
|
+
- Coupling and cohesion
|
|
619
|
+
- Dependency direction
|
|
620
|
+
- Layering violations
|
|
621
|
+
|
|
622
|
+
4. Scalability
|
|
623
|
+
- Extensibility points
|
|
624
|
+
- Future maintenance burden
|
|
625
|
+
- Breaking changes
|
|
626
|
+
|
|
627
|
+
Provide specific refactoring suggestions with before/after examples."""
|
|
628
|
+
|
|
629
|
+
quality_fallback = """You are the Quality Analyst, a code quality expert.
|
|
630
|
+
|
|
631
|
+
Your focus areas:
|
|
632
|
+
1. Code Smells
|
|
633
|
+
- Long methods/functions
|
|
634
|
+
- Large classes
|
|
635
|
+
- Duplicate code
|
|
636
|
+
- Dead code
|
|
637
|
+
- Magic numbers/strings
|
|
638
|
+
|
|
639
|
+
2. Maintainability
|
|
640
|
+
- Clear naming
|
|
641
|
+
- Appropriate comments
|
|
642
|
+
- Consistent formatting
|
|
643
|
+
- Error handling
|
|
644
|
+
- Logging
|
|
645
|
+
|
|
646
|
+
3. Testing
|
|
647
|
+
- Test coverage gaps
|
|
648
|
+
- Edge cases
|
|
649
|
+
- Error scenarios
|
|
650
|
+
- Integration points
|
|
651
|
+
|
|
652
|
+
4. Complexity
|
|
653
|
+
- Cyclomatic complexity
|
|
654
|
+
- Nesting depth
|
|
655
|
+
- Parameter counts
|
|
656
|
+
- Cognitive load
|
|
657
|
+
|
|
658
|
+
Prioritize issues that affect long-term maintainability."""
|
|
659
|
+
|
|
660
|
+
# 2. Security Analyst
|
|
661
|
+
self._agents["security"] = self._factory.create_agent(
|
|
662
|
+
name="security_analyst",
|
|
663
|
+
role="security",
|
|
664
|
+
description="Expert at identifying security vulnerabilities",
|
|
665
|
+
system_prompt=self._get_system_prompt("security_analyst", security_fallback),
|
|
666
|
+
model_tier=self.config.security_tier,
|
|
667
|
+
memory_graph_enabled=self.config.memory_graph_enabled,
|
|
668
|
+
memory_graph_path=self.config.memory_graph_path,
|
|
669
|
+
)
|
|
670
|
+
|
|
671
|
+
# 3. Architecture Reviewer
|
|
672
|
+
self._agents["architecture"] = self._factory.create_agent(
|
|
673
|
+
name="architecture_reviewer",
|
|
674
|
+
role="architect",
|
|
675
|
+
description="Evaluates code design and architecture",
|
|
676
|
+
system_prompt=self._get_system_prompt("architecture_reviewer", architecture_fallback),
|
|
677
|
+
model_tier=self.config.architecture_tier,
|
|
678
|
+
memory_graph_enabled=self.config.memory_graph_enabled,
|
|
679
|
+
memory_graph_path=self.config.memory_graph_path,
|
|
680
|
+
)
|
|
681
|
+
|
|
682
|
+
# 4. Quality Analyst
|
|
683
|
+
self._agents["quality"] = self._factory.create_agent(
|
|
684
|
+
name="quality_analyst",
|
|
685
|
+
role="analyst",
|
|
686
|
+
description="Identifies code quality and maintainability issues",
|
|
687
|
+
system_prompt=self._get_system_prompt("quality_analyst", quality_fallback),
|
|
688
|
+
model_tier=self.config.quality_tier,
|
|
689
|
+
memory_graph_enabled=self.config.memory_graph_enabled,
|
|
690
|
+
memory_graph_path=self.config.memory_graph_path,
|
|
691
|
+
)
|
|
692
|
+
|
|
693
|
+
# Performance fallback
|
|
694
|
+
performance_fallback = """You are the Performance Reviewer, a performance engineer.
|
|
695
|
+
|
|
696
|
+
Your focus areas:
|
|
697
|
+
1. Algorithm Efficiency
|
|
698
|
+
- Time complexity (Big O)
|
|
699
|
+
- Space complexity
|
|
700
|
+
- Unnecessary iterations
|
|
701
|
+
- Inefficient data structures
|
|
702
|
+
|
|
703
|
+
2. Resource Usage
|
|
704
|
+
- Memory leaks
|
|
705
|
+
- Connection leaks
|
|
706
|
+
- File handle management
|
|
707
|
+
- Cache misuse
|
|
708
|
+
|
|
709
|
+
3. Common Anti-patterns
|
|
710
|
+
- N+1 queries
|
|
711
|
+
- Sync operations in async code
|
|
712
|
+
- Blocking main thread
|
|
713
|
+
- Unoptimized regex
|
|
714
|
+
- String concatenation in loops
|
|
715
|
+
|
|
716
|
+
4. Database Performance
|
|
717
|
+
- Missing indexes
|
|
718
|
+
- Expensive queries
|
|
719
|
+
- Over-fetching
|
|
720
|
+
- Transaction scope
|
|
721
|
+
|
|
722
|
+
Provide optimization suggestions with expected impact."""
|
|
723
|
+
|
|
724
|
+
# 5. Performance Reviewer
|
|
725
|
+
self._agents["performance"] = self._factory.create_agent(
|
|
726
|
+
name="performance_reviewer",
|
|
727
|
+
role="analyst",
|
|
728
|
+
description="Identifies performance issues and optimizations",
|
|
729
|
+
system_prompt=self._get_system_prompt("performance_reviewer", performance_fallback),
|
|
730
|
+
model_tier=self.config.performance_tier,
|
|
731
|
+
memory_graph_enabled=self.config.memory_graph_enabled,
|
|
732
|
+
memory_graph_path=self.config.memory_graph_path,
|
|
733
|
+
)
|
|
734
|
+
|
|
735
|
+
async def _create_workflow(self) -> None:
|
|
736
|
+
"""Create hierarchical workflow with Review Lead as manager."""
|
|
737
|
+
agents = list(self._agents.values())
|
|
738
|
+
|
|
739
|
+
self._workflow = self._factory.create_workflow(
|
|
740
|
+
name="code_review_workflow",
|
|
741
|
+
agents=agents,
|
|
742
|
+
mode="hierarchical", # Review Lead delegates to others
|
|
743
|
+
description="Comprehensive code review with coordinated analysis",
|
|
744
|
+
)
|
|
745
|
+
|
|
746
|
+
async def review(
|
|
747
|
+
self,
|
|
748
|
+
diff: str = "",
|
|
749
|
+
files_changed: list[str] | None = None,
|
|
750
|
+
target: str = "",
|
|
751
|
+
context: dict | None = None,
|
|
752
|
+
) -> CodeReviewReport:
|
|
753
|
+
"""Perform a comprehensive code review.
|
|
754
|
+
|
|
755
|
+
Args:
|
|
756
|
+
diff: Git diff or code changes to review
|
|
757
|
+
files_changed: List of changed file paths
|
|
758
|
+
target: Description of review target
|
|
759
|
+
context: Optional context (previous findings, focus areas, etc.)
|
|
760
|
+
|
|
761
|
+
Returns:
|
|
762
|
+
CodeReviewReport with findings and verdict
|
|
763
|
+
|
|
764
|
+
"""
|
|
765
|
+
import time
|
|
766
|
+
|
|
767
|
+
start_time = time.time()
|
|
768
|
+
|
|
769
|
+
# Initialize if needed
|
|
770
|
+
await self._initialize()
|
|
771
|
+
|
|
772
|
+
context = context or {}
|
|
773
|
+
files_changed = files_changed or []
|
|
774
|
+
findings: list[ReviewFinding] = []
|
|
775
|
+
memory_hits = 0
|
|
776
|
+
|
|
777
|
+
# Build target description
|
|
778
|
+
if not target:
|
|
779
|
+
target = f"Review of {len(files_changed)} files"
|
|
780
|
+
|
|
781
|
+
# Check Memory Graph for similar past reviews
|
|
782
|
+
if self._graph and self.config.memory_graph_enabled:
|
|
783
|
+
try:
|
|
784
|
+
similar = self._graph.find_similar(
|
|
785
|
+
{"name": f"code_review:{target}", "description": target},
|
|
786
|
+
threshold=0.4,
|
|
787
|
+
limit=10,
|
|
788
|
+
)
|
|
789
|
+
if similar:
|
|
790
|
+
memory_hits = len(similar)
|
|
791
|
+
context["similar_reviews"] = [
|
|
792
|
+
{
|
|
793
|
+
"name": node.name,
|
|
794
|
+
"verdict": node.metadata.get("verdict", "unknown"),
|
|
795
|
+
"quality_score": node.metadata.get("quality_score", 0),
|
|
796
|
+
}
|
|
797
|
+
for node, score in similar
|
|
798
|
+
]
|
|
799
|
+
logger.info(f"Found {memory_hits} similar past reviews")
|
|
800
|
+
except Exception as e:
|
|
801
|
+
logger.warning(f"Error querying Memory Graph: {e}")
|
|
802
|
+
|
|
803
|
+
# Build review task for the crew
|
|
804
|
+
review_task = self._build_review_task(diff, files_changed, context)
|
|
805
|
+
|
|
806
|
+
# Execute the workflow
|
|
807
|
+
verdict = Verdict.APPROVE
|
|
808
|
+
try:
|
|
809
|
+
result = await self._workflow.run(review_task, initial_state=context)
|
|
810
|
+
|
|
811
|
+
# Parse findings from result
|
|
812
|
+
findings = self._parse_findings(result)
|
|
813
|
+
|
|
814
|
+
# Determine verdict
|
|
815
|
+
verdict = self._determine_verdict(findings)
|
|
816
|
+
|
|
817
|
+
except Exception as e:
|
|
818
|
+
logger.error(f"Code review failed: {e}")
|
|
819
|
+
# Return partial report with error
|
|
820
|
+
return CodeReviewReport(
|
|
821
|
+
target=target,
|
|
822
|
+
findings=findings,
|
|
823
|
+
verdict=Verdict.REQUEST_CHANGES,
|
|
824
|
+
summary=f"Review failed with error: {e}",
|
|
825
|
+
review_duration_seconds=time.time() - start_time,
|
|
826
|
+
agents_used=list(self._agents.keys()),
|
|
827
|
+
memory_graph_hits=memory_hits,
|
|
828
|
+
metadata={"error": str(e)},
|
|
829
|
+
)
|
|
830
|
+
|
|
831
|
+
# Build the report
|
|
832
|
+
duration = time.time() - start_time
|
|
833
|
+
report = CodeReviewReport(
|
|
834
|
+
target=target,
|
|
835
|
+
findings=findings,
|
|
836
|
+
verdict=verdict,
|
|
837
|
+
summary=self._generate_summary(findings, verdict),
|
|
838
|
+
review_duration_seconds=duration,
|
|
839
|
+
agents_used=list(self._agents.keys()),
|
|
840
|
+
memory_graph_hits=memory_hits,
|
|
841
|
+
metadata={
|
|
842
|
+
"review_depth": self.config.review_depth,
|
|
843
|
+
"framework": str(self._factory.framework.value),
|
|
844
|
+
"files_changed": files_changed,
|
|
845
|
+
},
|
|
846
|
+
)
|
|
847
|
+
|
|
848
|
+
# Store review in Memory Graph
|
|
849
|
+
if self._graph and self.config.memory_graph_enabled:
|
|
850
|
+
try:
|
|
851
|
+
self._graph.add_finding(
|
|
852
|
+
"code_review_crew",
|
|
853
|
+
{
|
|
854
|
+
"type": "code_review",
|
|
855
|
+
"name": f"review:{target}",
|
|
856
|
+
"description": report.summary,
|
|
857
|
+
"verdict": verdict.value,
|
|
858
|
+
"quality_score": report.quality_score,
|
|
859
|
+
"findings_count": len(findings),
|
|
860
|
+
},
|
|
861
|
+
)
|
|
862
|
+
self._graph._save()
|
|
863
|
+
except Exception as e:
|
|
864
|
+
logger.warning(f"Error storing review in Memory Graph: {e}")
|
|
865
|
+
|
|
866
|
+
return report
|
|
867
|
+
|
|
868
|
+
def _build_review_task(self, diff: str, files_changed: list[str], context: dict) -> str:
|
|
869
|
+
"""Build the review task description for the crew."""
|
|
870
|
+
depth_instructions = {
|
|
871
|
+
"quick": "Focus on critical issues only. Skip style and minor issues.",
|
|
872
|
+
"standard": "Cover security, architecture, quality, and performance.",
|
|
873
|
+
"thorough": "Deep review including edge cases, testing, and docs.",
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
focus_list = ", ".join(self.config.focus_areas)
|
|
877
|
+
|
|
878
|
+
task = f"""Perform a comprehensive code review.
|
|
879
|
+
|
|
880
|
+
Review Depth: {self.config.review_depth}
|
|
881
|
+
Instructions: {depth_instructions.get(self.config.review_depth, "standard")}
|
|
882
|
+
Focus Areas: {focus_list}
|
|
883
|
+
|
|
884
|
+
Files Changed ({len(files_changed)}):
|
|
885
|
+
{chr(10).join(f" - {f}" for f in files_changed[:20])}
|
|
886
|
+
|
|
887
|
+
Diff/Code to Review:
|
|
888
|
+
```
|
|
889
|
+
{diff[:15000]}
|
|
890
|
+
```
|
|
891
|
+
|
|
892
|
+
Workflow:
|
|
893
|
+
1. Review Lead coordinates the overall review strategy
|
|
894
|
+
2. Security Analyst checks for security vulnerabilities
|
|
895
|
+
3. Architecture Reviewer evaluates design and structure
|
|
896
|
+
4. Quality Analyst identifies code quality issues
|
|
897
|
+
5. Performance Reviewer spots performance problems
|
|
898
|
+
|
|
899
|
+
For each finding, provide:
|
|
900
|
+
- Title and description
|
|
901
|
+
- Severity (critical/high/medium/low/info)
|
|
902
|
+
- Category (security/architecture/quality/performance/etc.)
|
|
903
|
+
- File path and line number
|
|
904
|
+
- Specific suggestion with code example if applicable
|
|
905
|
+
|
|
906
|
+
Final Verdict Options:
|
|
907
|
+
- APPROVE: No issues or only minor suggestions
|
|
908
|
+
- APPROVE_WITH_SUGGESTIONS: Good overall, improvements recommended
|
|
909
|
+
- REQUEST_CHANGES: Issues must be addressed before merge
|
|
910
|
+
- REJECT: Fundamental problems requiring rework
|
|
911
|
+
|
|
912
|
+
"""
|
|
913
|
+
if context.get("similar_reviews"):
|
|
914
|
+
task += f"""
|
|
915
|
+
Similar Past Reviews Found: {len(context["similar_reviews"])}
|
|
916
|
+
Consider patterns from past reviews.
|
|
917
|
+
"""
|
|
918
|
+
|
|
919
|
+
return task
|
|
920
|
+
|
|
921
|
+
def _parse_findings(self, result: dict) -> list[ReviewFinding]:
|
|
922
|
+
"""Parse findings from workflow result."""
|
|
923
|
+
findings = []
|
|
924
|
+
|
|
925
|
+
output = result.get("output", "")
|
|
926
|
+
metadata = result.get("metadata", {})
|
|
927
|
+
|
|
928
|
+
# Check for structured findings in metadata
|
|
929
|
+
if "findings" in metadata:
|
|
930
|
+
for f in metadata["findings"]:
|
|
931
|
+
findings.append(self._dict_to_finding(f))
|
|
932
|
+
return findings
|
|
933
|
+
|
|
934
|
+
# Parse from text output (fallback)
|
|
935
|
+
findings = self._parse_text_findings(output)
|
|
936
|
+
|
|
937
|
+
return findings
|
|
938
|
+
|
|
939
|
+
def _dict_to_finding(self, data: dict) -> ReviewFinding:
|
|
940
|
+
"""Convert dictionary to ReviewFinding."""
|
|
941
|
+
return ReviewFinding(
|
|
942
|
+
title=data.get("title", "Untitled Finding"),
|
|
943
|
+
description=data.get("description", ""),
|
|
944
|
+
severity=Severity(data.get("severity", "medium")),
|
|
945
|
+
category=FindingCategory(data.get("category", "other")),
|
|
946
|
+
file_path=data.get("file_path"),
|
|
947
|
+
line_number=data.get("line_number"),
|
|
948
|
+
code_snippet=data.get("code_snippet"),
|
|
949
|
+
suggestion=data.get("suggestion"),
|
|
950
|
+
before_code=data.get("before_code"),
|
|
951
|
+
after_code=data.get("after_code"),
|
|
952
|
+
confidence=data.get("confidence", 1.0),
|
|
953
|
+
metadata=data.get("metadata", {}),
|
|
954
|
+
)
|
|
955
|
+
|
|
956
|
+
def _parse_text_findings(self, text: str) -> list[ReviewFinding]:
|
|
957
|
+
"""Parse findings from unstructured text output."""
|
|
958
|
+
findings = []
|
|
959
|
+
|
|
960
|
+
severity_keywords = {
|
|
961
|
+
Severity.CRITICAL: ["critical", "security", "vulnerability"],
|
|
962
|
+
Severity.HIGH: ["high", "important", "must fix"],
|
|
963
|
+
Severity.MEDIUM: ["medium", "should", "consider"],
|
|
964
|
+
Severity.LOW: ["low", "minor", "nitpick"],
|
|
965
|
+
Severity.INFO: ["info", "suggestion", "optional"],
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
category_keywords = {
|
|
969
|
+
FindingCategory.SECURITY: ["security", "injection", "xss", "auth"],
|
|
970
|
+
FindingCategory.ARCHITECTURE: ["architecture", "design", "solid"],
|
|
971
|
+
FindingCategory.QUALITY: ["quality", "smell", "duplicate"],
|
|
972
|
+
FindingCategory.PERFORMANCE: ["performance", "slow", "optimize"],
|
|
973
|
+
FindingCategory.TESTING: ["test", "coverage", "assertion"],
|
|
974
|
+
FindingCategory.DOCUMENTATION: ["doc", "comment", "readme"],
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
lines = text.split("\n")
|
|
978
|
+
current_finding = None
|
|
979
|
+
|
|
980
|
+
for line in lines:
|
|
981
|
+
line_lower = line.lower().strip()
|
|
982
|
+
|
|
983
|
+
# Detect severity
|
|
984
|
+
detected_severity = Severity.MEDIUM
|
|
985
|
+
for sev, keywords in severity_keywords.items():
|
|
986
|
+
if any(kw in line_lower for kw in keywords):
|
|
987
|
+
detected_severity = sev
|
|
988
|
+
break
|
|
989
|
+
|
|
990
|
+
# Detect category
|
|
991
|
+
detected_category = FindingCategory.OTHER
|
|
992
|
+
for cat, keywords in category_keywords.items():
|
|
993
|
+
if any(kw in line_lower for kw in keywords):
|
|
994
|
+
detected_category = cat
|
|
995
|
+
break
|
|
996
|
+
|
|
997
|
+
# Simple finding detection
|
|
998
|
+
if any(
|
|
999
|
+
indicator in line_lower
|
|
1000
|
+
for indicator in ["issue", "finding", "problem", "fix", "should"]
|
|
1001
|
+
):
|
|
1002
|
+
if current_finding:
|
|
1003
|
+
findings.append(current_finding)
|
|
1004
|
+
|
|
1005
|
+
current_finding = ReviewFinding(
|
|
1006
|
+
title=line[:100].strip(),
|
|
1007
|
+
description=line,
|
|
1008
|
+
severity=detected_severity,
|
|
1009
|
+
category=detected_category,
|
|
1010
|
+
)
|
|
1011
|
+
|
|
1012
|
+
if current_finding:
|
|
1013
|
+
findings.append(current_finding)
|
|
1014
|
+
|
|
1015
|
+
return findings
|
|
1016
|
+
|
|
1017
|
+
def _determine_verdict(self, findings: list[ReviewFinding]) -> Verdict:
|
|
1018
|
+
"""Determine review verdict based on findings."""
|
|
1019
|
+
if not findings:
|
|
1020
|
+
return Verdict.APPROVE
|
|
1021
|
+
|
|
1022
|
+
critical_count = len([f for f in findings if f.severity == Severity.CRITICAL])
|
|
1023
|
+
high_count = len([f for f in findings if f.severity == Severity.HIGH])
|
|
1024
|
+
medium_count = len([f for f in findings if f.severity == Severity.MEDIUM])
|
|
1025
|
+
|
|
1026
|
+
# Reject if too many critical issues
|
|
1027
|
+
if critical_count >= 3:
|
|
1028
|
+
return Verdict.REJECT
|
|
1029
|
+
|
|
1030
|
+
# Request changes for critical or many high issues
|
|
1031
|
+
if critical_count > 0 or high_count > 3:
|
|
1032
|
+
return Verdict.REQUEST_CHANGES
|
|
1033
|
+
|
|
1034
|
+
# Approve with suggestions for medium/low issues
|
|
1035
|
+
if high_count > 0 or medium_count > 0:
|
|
1036
|
+
return Verdict.APPROVE_WITH_SUGGESTIONS
|
|
1037
|
+
|
|
1038
|
+
return Verdict.APPROVE
|
|
1039
|
+
|
|
1040
|
+
def _generate_summary(self, findings: list[ReviewFinding], verdict: Verdict) -> str:
|
|
1041
|
+
"""Generate executive summary of review."""
|
|
1042
|
+
if not findings:
|
|
1043
|
+
return "Code review passed with no issues identified."
|
|
1044
|
+
|
|
1045
|
+
critical = sum(1 for f in findings if f.severity == Severity.CRITICAL)
|
|
1046
|
+
high = sum(1 for f in findings if f.severity == Severity.HIGH)
|
|
1047
|
+
medium = sum(1 for f in findings if f.severity == Severity.MEDIUM)
|
|
1048
|
+
low = sum(1 for f in findings if f.severity == Severity.LOW)
|
|
1049
|
+
|
|
1050
|
+
verdict_text = {
|
|
1051
|
+
Verdict.APPROVE: "Approved - ready to merge",
|
|
1052
|
+
Verdict.APPROVE_WITH_SUGGESTIONS: "Approved with suggestions",
|
|
1053
|
+
Verdict.REQUEST_CHANGES: "Changes requested before merge",
|
|
1054
|
+
Verdict.REJECT: "Rejected - requires significant rework",
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
summary_parts = [
|
|
1058
|
+
f"Code review verdict: {verdict_text.get(verdict, verdict.value)}",
|
|
1059
|
+
f"Total findings: {len(findings)}",
|
|
1060
|
+
]
|
|
1061
|
+
|
|
1062
|
+
if critical > 0:
|
|
1063
|
+
summary_parts.append(f" - {critical} CRITICAL (blocking)")
|
|
1064
|
+
if high > 0:
|
|
1065
|
+
summary_parts.append(f" - {high} HIGH (should address)")
|
|
1066
|
+
if medium > 0:
|
|
1067
|
+
summary_parts.append(f" - {medium} MEDIUM (recommended)")
|
|
1068
|
+
if low > 0:
|
|
1069
|
+
summary_parts.append(f" - {low} LOW (nice to have)")
|
|
1070
|
+
|
|
1071
|
+
# Add top categories
|
|
1072
|
+
by_category: dict[str, int] = {}
|
|
1073
|
+
for f in findings:
|
|
1074
|
+
cat = f.category.value
|
|
1075
|
+
by_category[cat] = by_category.get(cat, 0) + 1
|
|
1076
|
+
|
|
1077
|
+
if by_category:
|
|
1078
|
+
top_cats = sorted(by_category.items(), key=lambda x: x[1], reverse=True)[:3]
|
|
1079
|
+
summary_parts.append("\nTop issue categories:")
|
|
1080
|
+
for cat, count in top_cats:
|
|
1081
|
+
summary_parts.append(f" - {cat}: {count}")
|
|
1082
|
+
|
|
1083
|
+
return "\n".join(summary_parts)
|
|
1084
|
+
|
|
1085
|
+
@property
|
|
1086
|
+
def agents(self) -> dict[str, Any]:
|
|
1087
|
+
"""Get the crew's agents."""
|
|
1088
|
+
return self._agents
|
|
1089
|
+
|
|
1090
|
+
@property
|
|
1091
|
+
def is_initialized(self) -> bool:
|
|
1092
|
+
"""Check if crew is initialized."""
|
|
1093
|
+
return self._initialized
|
|
1094
|
+
|
|
1095
|
+
async def get_agent_stats(self) -> dict:
|
|
1096
|
+
"""Get statistics about crew agents."""
|
|
1097
|
+
await self._initialize()
|
|
1098
|
+
|
|
1099
|
+
agents_dict: dict = {}
|
|
1100
|
+
stats: dict = {
|
|
1101
|
+
"agent_count": len(self._agents),
|
|
1102
|
+
"agents": agents_dict,
|
|
1103
|
+
"framework": self._factory.framework.value if self._factory else "unknown",
|
|
1104
|
+
"memory_graph_enabled": self.config.memory_graph_enabled,
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
for name, agent in self._agents.items():
|
|
1108
|
+
agents_dict[name] = {
|
|
1109
|
+
"role": agent.config.role if hasattr(agent, "config") else "unknown",
|
|
1110
|
+
"model_tier": getattr(agent.config, "model_tier", "unknown"),
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
return stats
|