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,312 @@
|
|
|
1
|
+
"""Code Review Adapters for CodeReviewCrew Integration
|
|
2
|
+
|
|
3
|
+
Provides format conversion functions between CodeReviewCrew output
|
|
4
|
+
and workflow dict formats used by existing workflows.
|
|
5
|
+
|
|
6
|
+
Copyright 2025 Smart-AI-Memory
|
|
7
|
+
Licensed under Fair Source License 0.9
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import asyncio
|
|
11
|
+
import logging
|
|
12
|
+
from typing import TYPE_CHECKING
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from attune_llm.agent_factory.crews import CodeReviewReport
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _check_crew_available() -> bool:
|
|
21
|
+
"""Check if CodeReviewCrew is available.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
True if the crew module can be imported, False otherwise.
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
try:
|
|
28
|
+
from attune_llm.agent_factory.crews import CodeReviewCrew # noqa: F401
|
|
29
|
+
|
|
30
|
+
return True
|
|
31
|
+
except ImportError:
|
|
32
|
+
return False
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
async def _get_crew_review(
|
|
36
|
+
diff: str,
|
|
37
|
+
files_changed: list[str] | None = None,
|
|
38
|
+
config: dict | None = None,
|
|
39
|
+
timeout: float = 300.0,
|
|
40
|
+
) -> "CodeReviewReport | None":
|
|
41
|
+
"""Get CodeReviewCrew review results with graceful fallback.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
diff: Git diff or code to review
|
|
45
|
+
files_changed: List of changed files
|
|
46
|
+
config: Optional CodeReviewConfig parameters as dict
|
|
47
|
+
timeout: Maximum time to wait for review (default 5 minutes)
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
CodeReviewReport if successful, None if crew unavailable or failed.
|
|
51
|
+
|
|
52
|
+
"""
|
|
53
|
+
if not _check_crew_available():
|
|
54
|
+
logger.debug("CodeReviewCrew not available, returning None")
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
from attune_llm.agent_factory.crews import CodeReviewConfig, CodeReviewCrew
|
|
59
|
+
|
|
60
|
+
# Build config from dict
|
|
61
|
+
crew_config = CodeReviewConfig(**(config or {}))
|
|
62
|
+
crew = CodeReviewCrew(config=crew_config)
|
|
63
|
+
|
|
64
|
+
# Run with timeout
|
|
65
|
+
report = await asyncio.wait_for(
|
|
66
|
+
crew.review(diff=diff, files_changed=files_changed or []),
|
|
67
|
+
timeout=timeout,
|
|
68
|
+
)
|
|
69
|
+
return report
|
|
70
|
+
|
|
71
|
+
except asyncio.TimeoutError:
|
|
72
|
+
logger.warning(f"CodeReviewCrew review timed out after {timeout}s")
|
|
73
|
+
return None
|
|
74
|
+
except Exception as e:
|
|
75
|
+
logger.warning(f"CodeReviewCrew review failed: {e}")
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def crew_report_to_workflow_format(report: "CodeReviewReport") -> dict:
|
|
80
|
+
"""Convert CodeReviewReport to workflow dict format.
|
|
81
|
+
|
|
82
|
+
This converts the crew's structured output to the format expected
|
|
83
|
+
by existing workflows (matching CodeReviewWorkflow output).
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
report: CodeReviewReport from CodeReviewCrew
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
Dict in workflow format with findings, verdict, etc.
|
|
90
|
+
|
|
91
|
+
"""
|
|
92
|
+
findings = []
|
|
93
|
+
for finding in report.findings:
|
|
94
|
+
finding_dict = {
|
|
95
|
+
"type": finding.category.value if finding.category else "other",
|
|
96
|
+
"title": finding.title,
|
|
97
|
+
"description": finding.description,
|
|
98
|
+
"severity": finding.severity.value if finding.severity else "medium",
|
|
99
|
+
"file": finding.file_path,
|
|
100
|
+
"line": finding.line_number,
|
|
101
|
+
"code_snippet": finding.code_snippet,
|
|
102
|
+
"suggestion": finding.suggestion,
|
|
103
|
+
"before_code": finding.before_code,
|
|
104
|
+
"after_code": finding.after_code,
|
|
105
|
+
"confidence": finding.confidence,
|
|
106
|
+
}
|
|
107
|
+
findings.append(finding_dict)
|
|
108
|
+
|
|
109
|
+
# Build severity breakdown (using sum with generator for memory efficiency)
|
|
110
|
+
severity_counts = {
|
|
111
|
+
"critical": sum(1 for f in report.findings if f.severity.value == "critical"),
|
|
112
|
+
"high": sum(1 for f in report.findings if f.severity.value == "high"),
|
|
113
|
+
"medium": sum(1 for f in report.findings if f.severity.value == "medium"),
|
|
114
|
+
"low": sum(1 for f in report.findings if f.severity.value == "low"),
|
|
115
|
+
"info": sum(1 for f in report.findings if f.severity.value == "info"),
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
# Build category breakdown
|
|
119
|
+
by_category: dict[str, int] = {}
|
|
120
|
+
for finding in report.findings:
|
|
121
|
+
cat = finding.category.value
|
|
122
|
+
by_category[cat] = by_category.get(cat, 0) + 1
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
"crew_enabled": True,
|
|
126
|
+
"findings": findings,
|
|
127
|
+
"finding_count": len(findings),
|
|
128
|
+
"verdict": report.verdict.value,
|
|
129
|
+
"quality_score": report.quality_score,
|
|
130
|
+
"has_blocking_issues": report.has_blocking_issues,
|
|
131
|
+
"summary": report.summary,
|
|
132
|
+
"assessment": {
|
|
133
|
+
"quality_score": report.quality_score,
|
|
134
|
+
"verdict": report.verdict.value,
|
|
135
|
+
"severity_breakdown": severity_counts,
|
|
136
|
+
"by_category": by_category,
|
|
137
|
+
"critical_findings": [f for f in findings if f["severity"] == "critical"],
|
|
138
|
+
"high_findings": [f for f in findings if f["severity"] == "high"],
|
|
139
|
+
},
|
|
140
|
+
"agents_used": report.agents_used,
|
|
141
|
+
"memory_graph_hits": report.memory_graph_hits,
|
|
142
|
+
"review_duration_seconds": report.review_duration_seconds,
|
|
143
|
+
"metadata": report.metadata,
|
|
144
|
+
# Pass through cost if tracked by crew (future enhancement)
|
|
145
|
+
"cost": report.metadata.get("cost", 0.0),
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def workflow_findings_to_crew_format(findings: list[dict]) -> list[dict]:
|
|
150
|
+
"""Convert workflow findings to ReviewFinding-compatible dicts.
|
|
151
|
+
|
|
152
|
+
This is useful when passing workflow findings to CodeReviewCrew
|
|
153
|
+
for enhanced analysis.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
findings: List of finding dicts from workflow
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
List of dicts that can be used with CodeReviewCrew context.
|
|
160
|
+
|
|
161
|
+
"""
|
|
162
|
+
crew_findings = []
|
|
163
|
+
for finding in findings:
|
|
164
|
+
crew_finding = {
|
|
165
|
+
"title": finding.get("title", finding.get("type", "Unknown")),
|
|
166
|
+
"description": finding.get("description", finding.get("match", "")),
|
|
167
|
+
"severity": finding.get("severity", "medium"),
|
|
168
|
+
"category": _map_type_to_category(finding.get("type", "other")),
|
|
169
|
+
"file_path": finding.get("file"),
|
|
170
|
+
"line_number": finding.get("line"),
|
|
171
|
+
"code_snippet": finding.get("code_snippet") or finding.get("match"),
|
|
172
|
+
"suggestion": finding.get("suggestion") or finding.get("remediation"),
|
|
173
|
+
"confidence": finding.get("confidence", 1.0),
|
|
174
|
+
}
|
|
175
|
+
crew_findings.append(crew_finding)
|
|
176
|
+
return crew_findings
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def merge_code_review_results(
|
|
180
|
+
crew_report: dict | None,
|
|
181
|
+
workflow_findings: dict | None,
|
|
182
|
+
) -> dict:
|
|
183
|
+
"""Merge CodeReviewCrew and workflow code review results.
|
|
184
|
+
|
|
185
|
+
Combines findings from both sources, deduplicating where possible,
|
|
186
|
+
and provides a unified assessment.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
crew_report: Crew results in workflow format (from crew_report_to_workflow_format)
|
|
190
|
+
workflow_findings: Workflow code review results
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
Merged dict with combined findings and assessment.
|
|
194
|
+
|
|
195
|
+
"""
|
|
196
|
+
# Handle None cases
|
|
197
|
+
if crew_report is None and workflow_findings is None:
|
|
198
|
+
return {
|
|
199
|
+
"findings": [],
|
|
200
|
+
"quality_score": 100,
|
|
201
|
+
"verdict": "approve",
|
|
202
|
+
"merged": False,
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if crew_report is None and workflow_findings is not None:
|
|
206
|
+
return {**workflow_findings, "merged": False, "crew_enabled": False}
|
|
207
|
+
|
|
208
|
+
if workflow_findings is None and crew_report is not None:
|
|
209
|
+
return {**crew_report, "merged": False}
|
|
210
|
+
|
|
211
|
+
# At this point, both should be non-None
|
|
212
|
+
assert crew_report is not None
|
|
213
|
+
assert workflow_findings is not None
|
|
214
|
+
|
|
215
|
+
# Merge findings (prefer crew findings for duplicates)
|
|
216
|
+
crew_findings = crew_report.get("findings", [])
|
|
217
|
+
wf_findings = workflow_findings.get("findings", [])
|
|
218
|
+
|
|
219
|
+
# Build set of crew finding keys for deduplication
|
|
220
|
+
crew_keys = set()
|
|
221
|
+
for f in crew_findings:
|
|
222
|
+
key = (f.get("file"), f.get("line"), f.get("type"))
|
|
223
|
+
crew_keys.add(key)
|
|
224
|
+
|
|
225
|
+
# Add non-duplicate workflow findings
|
|
226
|
+
merged_findings = list(crew_findings)
|
|
227
|
+
for f in wf_findings:
|
|
228
|
+
key = (f.get("file"), f.get("line"), f.get("type"))
|
|
229
|
+
if key not in crew_keys:
|
|
230
|
+
merged_findings.append(f)
|
|
231
|
+
|
|
232
|
+
# Calculate merged quality score (weighted average)
|
|
233
|
+
crew_score = crew_report.get("quality_score", 100)
|
|
234
|
+
wf_score = workflow_findings.get("security_score", 90)
|
|
235
|
+
# Give crew score higher weight (more comprehensive)
|
|
236
|
+
merged_score = (crew_score * 0.7 + wf_score * 0.3) if wf_score else crew_score
|
|
237
|
+
|
|
238
|
+
# Determine verdict (take more severe)
|
|
239
|
+
crew_verdict = crew_report.get("verdict", "approve")
|
|
240
|
+
wf_verdict = workflow_findings.get("verdict", "approve")
|
|
241
|
+
verdict = _merge_verdicts(crew_verdict, wf_verdict)
|
|
242
|
+
|
|
243
|
+
# Merge severity counts
|
|
244
|
+
crew_severity = crew_report.get("assessment", {}).get("severity_breakdown", {})
|
|
245
|
+
wf_severity = workflow_findings.get("assessment", {}).get("severity_breakdown", {})
|
|
246
|
+
merged_severity = {
|
|
247
|
+
"critical": max(crew_severity.get("critical", 0), wf_severity.get("critical", 0)),
|
|
248
|
+
"high": max(crew_severity.get("high", 0), wf_severity.get("high", 0)),
|
|
249
|
+
"medium": max(crew_severity.get("medium", 0), wf_severity.get("medium", 0)),
|
|
250
|
+
"low": max(crew_severity.get("low", 0), wf_severity.get("low", 0)),
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
"merged": True,
|
|
255
|
+
"crew_enabled": True,
|
|
256
|
+
"findings": merged_findings,
|
|
257
|
+
"finding_count": len(merged_findings),
|
|
258
|
+
"quality_score": merged_score,
|
|
259
|
+
"verdict": verdict,
|
|
260
|
+
"has_blocking_issues": (
|
|
261
|
+
merged_severity.get("critical", 0) > 0 or merged_severity.get("high", 0) > 3
|
|
262
|
+
),
|
|
263
|
+
"assessment": {
|
|
264
|
+
"quality_score": merged_score,
|
|
265
|
+
"verdict": verdict,
|
|
266
|
+
"severity_breakdown": merged_severity,
|
|
267
|
+
"critical_findings": [f for f in merged_findings if f.get("severity") == "critical"],
|
|
268
|
+
"high_findings": [f for f in merged_findings if f.get("severity") == "high"],
|
|
269
|
+
},
|
|
270
|
+
"crew_summary": crew_report.get("summary", ""),
|
|
271
|
+
"agents_used": crew_report.get("agents_used", []),
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _merge_verdicts(verdict1: str, verdict2: str) -> str:
|
|
276
|
+
"""Merge two verdicts, taking the more severe one."""
|
|
277
|
+
severity_order = ["reject", "request_changes", "approve_with_suggestions", "approve"]
|
|
278
|
+
# Optimization: Dict for O(1) lookup instead of O(n) .index() call
|
|
279
|
+
severity_map = {v: i for i, v in enumerate(severity_order)}
|
|
280
|
+
|
|
281
|
+
# Normalize verdicts
|
|
282
|
+
v1 = verdict1.lower().replace("-", "_")
|
|
283
|
+
v2 = verdict2.lower().replace("-", "_")
|
|
284
|
+
|
|
285
|
+
idx1 = severity_map.get(v1, 3)
|
|
286
|
+
idx2 = severity_map.get(v2, 3)
|
|
287
|
+
|
|
288
|
+
# Return more severe (lower index)
|
|
289
|
+
return severity_order[min(idx1, idx2)]
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def _map_type_to_category(vuln_type: str) -> str:
|
|
293
|
+
"""Map workflow vulnerability type to crew category."""
|
|
294
|
+
mapping = {
|
|
295
|
+
"sql_injection": "security",
|
|
296
|
+
"xss": "security",
|
|
297
|
+
"command_injection": "security",
|
|
298
|
+
"path_traversal": "security",
|
|
299
|
+
"hardcoded_secret": "security", # pragma: allowlist secret
|
|
300
|
+
"insecure_random": "security",
|
|
301
|
+
"code_smell": "quality",
|
|
302
|
+
"complexity": "quality",
|
|
303
|
+
"duplicate": "quality",
|
|
304
|
+
"performance": "performance",
|
|
305
|
+
"n_plus_one": "performance",
|
|
306
|
+
"architecture": "architecture",
|
|
307
|
+
"design": "architecture",
|
|
308
|
+
"solid": "architecture",
|
|
309
|
+
"test": "testing",
|
|
310
|
+
"coverage": "testing",
|
|
311
|
+
}
|
|
312
|
+
return mapping.get(vuln_type.lower(), "other")
|