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,285 @@
|
|
|
1
|
+
"""CLI commands for adaptive model routing statistics.
|
|
2
|
+
|
|
3
|
+
Provides commands to analyze model routing performance and get tier upgrade
|
|
4
|
+
recommendations based on historical telemetry data.
|
|
5
|
+
|
|
6
|
+
Copyright 2025 Smart-AI-Memory
|
|
7
|
+
Licensed under Fair Source License 0.9
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from attune.models import AdaptiveModelRouter
|
|
14
|
+
from attune.telemetry import UsageTracker
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def cmd_routing_stats(args: Any) -> int:
|
|
20
|
+
"""Show routing statistics for a workflow.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
args: Arguments with workflow, stage (optional), days
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
0 on success, 1 on error
|
|
27
|
+
"""
|
|
28
|
+
try:
|
|
29
|
+
# Get telemetry and router
|
|
30
|
+
tracker = UsageTracker.get_instance()
|
|
31
|
+
router = AdaptiveModelRouter(telemetry=tracker)
|
|
32
|
+
|
|
33
|
+
# Get routing stats
|
|
34
|
+
stats = router.get_routing_stats(
|
|
35
|
+
workflow=args.workflow,
|
|
36
|
+
stage=args.stage if hasattr(args, "stage") and args.stage else None,
|
|
37
|
+
days=args.days,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
if stats["total_calls"] == 0:
|
|
41
|
+
print(f"❌ No data found for workflow '{args.workflow}'")
|
|
42
|
+
print(f" (searched last {args.days} days)")
|
|
43
|
+
return 1
|
|
44
|
+
|
|
45
|
+
# Display stats
|
|
46
|
+
print("\n" + "=" * 70)
|
|
47
|
+
print(f"ADAPTIVE ROUTING STATISTICS - {stats['workflow']}")
|
|
48
|
+
if stats["stage"] != "all":
|
|
49
|
+
print(f"Stage: {stats['stage']}")
|
|
50
|
+
print("=" * 70)
|
|
51
|
+
|
|
52
|
+
print(f"\n📊 Overview (Last {stats['days_analyzed']} days)")
|
|
53
|
+
print(f" Total calls: {stats['total_calls']:,}")
|
|
54
|
+
print(f" Average cost: ${stats['avg_cost']:.4f}")
|
|
55
|
+
print(f" Average success rate: {stats['avg_success_rate']:.1%}")
|
|
56
|
+
print(f" Models used: {len(stats['models_used'])}")
|
|
57
|
+
|
|
58
|
+
# Per-model performance
|
|
59
|
+
print("\n📈 Per-Model Performance")
|
|
60
|
+
print("-" * 70)
|
|
61
|
+
|
|
62
|
+
for model in stats["models_used"]:
|
|
63
|
+
perf = stats["performance_by_model"][model]
|
|
64
|
+
print(f"\n {model}:")
|
|
65
|
+
print(f" Calls: {perf['calls']:,}")
|
|
66
|
+
print(f" Success rate: {perf['success_rate']:.1%}")
|
|
67
|
+
print(f" Avg cost: ${perf['avg_cost']:.4f}")
|
|
68
|
+
print(f" Avg latency: {perf['avg_latency_ms']:.0f}ms")
|
|
69
|
+
|
|
70
|
+
# Quality score calculation (from AdaptiveModelRouter)
|
|
71
|
+
quality_score = (perf["success_rate"] * 100) - (perf["avg_cost"] * 10)
|
|
72
|
+
print(f" Quality score: {quality_score:.2f}")
|
|
73
|
+
|
|
74
|
+
# Recommendations
|
|
75
|
+
print("\n💡 Recommendations")
|
|
76
|
+
print("-" * 70)
|
|
77
|
+
|
|
78
|
+
# Find best model
|
|
79
|
+
best_model = max(
|
|
80
|
+
stats["performance_by_model"].items(),
|
|
81
|
+
key=lambda x: (x[1]["success_rate"] * 100) - (x[1]["avg_cost"] * 10),
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
print(f" Best model: {best_model[0]}")
|
|
85
|
+
print(f" ({best_model[1]['success_rate']:.1%} success, ${best_model[1]['avg_cost']:.4f}/call)")
|
|
86
|
+
|
|
87
|
+
# Cost savings potential
|
|
88
|
+
if len(stats["models_used"]) > 1:
|
|
89
|
+
cheapest = min(
|
|
90
|
+
stats["performance_by_model"].items(),
|
|
91
|
+
key=lambda x: x[1]["avg_cost"],
|
|
92
|
+
)
|
|
93
|
+
most_expensive = max(
|
|
94
|
+
stats["performance_by_model"].items(),
|
|
95
|
+
key=lambda x: x[1]["avg_cost"],
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
if cheapest[0] != most_expensive[0]:
|
|
99
|
+
savings_per_call = most_expensive[1]["avg_cost"] - cheapest[1]["avg_cost"]
|
|
100
|
+
print("\n 💰 Potential savings:")
|
|
101
|
+
print(f" Using {cheapest[0]} instead of {most_expensive[0]}")
|
|
102
|
+
print(f" ${savings_per_call:.4f} per call")
|
|
103
|
+
if stats["total_calls"] > 0:
|
|
104
|
+
weekly_calls = (stats["total_calls"] / stats["days_analyzed"]) * 7
|
|
105
|
+
weekly_savings = savings_per_call * weekly_calls
|
|
106
|
+
print(f" ~${weekly_savings:.2f}/week potential")
|
|
107
|
+
|
|
108
|
+
return 0
|
|
109
|
+
|
|
110
|
+
except Exception as e:
|
|
111
|
+
logger.exception("Failed to get routing stats")
|
|
112
|
+
print(f"❌ Error: {e}")
|
|
113
|
+
return 1
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def cmd_routing_check(args: Any) -> int:
|
|
117
|
+
"""Check if tier upgrades are recommended for workflows.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
args: Arguments with workflow (or --all), stage (optional)
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
0 on success, 1 on error
|
|
124
|
+
"""
|
|
125
|
+
try:
|
|
126
|
+
# Get telemetry and router
|
|
127
|
+
tracker = UsageTracker.get_instance()
|
|
128
|
+
router = AdaptiveModelRouter(telemetry=tracker)
|
|
129
|
+
|
|
130
|
+
print("\n" + "=" * 70)
|
|
131
|
+
print("ADAPTIVE ROUTING - TIER UPGRADE RECOMMENDATIONS")
|
|
132
|
+
print("=" * 70)
|
|
133
|
+
|
|
134
|
+
if hasattr(args, "all") and args.all:
|
|
135
|
+
# Check all workflows
|
|
136
|
+
stats = tracker.get_stats(days=args.days)
|
|
137
|
+
workflows = list(stats["by_workflow"].keys())
|
|
138
|
+
|
|
139
|
+
if not workflows:
|
|
140
|
+
print("\n❌ No workflow data found")
|
|
141
|
+
return 1
|
|
142
|
+
|
|
143
|
+
print(f"\nChecking {len(workflows)} workflows (last {args.days} days)...\n")
|
|
144
|
+
|
|
145
|
+
upgrades_needed = []
|
|
146
|
+
upgrades_ok = []
|
|
147
|
+
|
|
148
|
+
for workflow_name in workflows:
|
|
149
|
+
should_upgrade, reason = router.recommend_tier_upgrade(
|
|
150
|
+
workflow=workflow_name, stage=None
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
if should_upgrade:
|
|
154
|
+
upgrades_needed.append((workflow_name, reason))
|
|
155
|
+
else:
|
|
156
|
+
upgrades_ok.append((workflow_name, reason))
|
|
157
|
+
|
|
158
|
+
# Show workflows needing upgrades
|
|
159
|
+
if upgrades_needed:
|
|
160
|
+
print(f"⚠️ {len(upgrades_needed)} workflow(s) need tier upgrade:")
|
|
161
|
+
print("-" * 70)
|
|
162
|
+
for workflow_name, reason in upgrades_needed:
|
|
163
|
+
print(f" • {workflow_name}")
|
|
164
|
+
print(f" {reason}")
|
|
165
|
+
print()
|
|
166
|
+
|
|
167
|
+
# Show workflows performing well
|
|
168
|
+
if upgrades_ok:
|
|
169
|
+
print(f"✓ {len(upgrades_ok)} workflow(s) performing well:")
|
|
170
|
+
print("-" * 70)
|
|
171
|
+
for workflow_name, reason in upgrades_ok:
|
|
172
|
+
print(f" • {workflow_name}: {reason}")
|
|
173
|
+
print()
|
|
174
|
+
|
|
175
|
+
# Summary
|
|
176
|
+
if upgrades_needed:
|
|
177
|
+
print("💡 Recommendation:")
|
|
178
|
+
print(" Enable adaptive routing to automatically upgrade tiers:")
|
|
179
|
+
print(" workflow = MyWorkflow(enable_adaptive_routing=True)")
|
|
180
|
+
return 0
|
|
181
|
+
else:
|
|
182
|
+
print("✓ All workflows performing well - no upgrades needed")
|
|
183
|
+
return 0
|
|
184
|
+
|
|
185
|
+
else:
|
|
186
|
+
# Check specific workflow
|
|
187
|
+
workflow_name = args.workflow
|
|
188
|
+
|
|
189
|
+
should_upgrade, reason = router.recommend_tier_upgrade(
|
|
190
|
+
workflow=workflow_name,
|
|
191
|
+
stage=args.stage if hasattr(args, "stage") and args.stage else None,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
print(f"\nWorkflow: {workflow_name}")
|
|
195
|
+
if hasattr(args, "stage") and args.stage:
|
|
196
|
+
print(f"Stage: {args.stage}")
|
|
197
|
+
print(f"Analysis period: Last {args.days} days")
|
|
198
|
+
print()
|
|
199
|
+
|
|
200
|
+
if should_upgrade:
|
|
201
|
+
print("⚠️ TIER UPGRADE RECOMMENDED")
|
|
202
|
+
print(f" {reason}")
|
|
203
|
+
print()
|
|
204
|
+
print("💡 Action:")
|
|
205
|
+
print(" 1. Enable adaptive routing:")
|
|
206
|
+
print(" workflow = MyWorkflow(enable_adaptive_routing=True)")
|
|
207
|
+
print(" 2. Or manually upgrade tier in workflow config")
|
|
208
|
+
return 0
|
|
209
|
+
else:
|
|
210
|
+
print("✓ NO UPGRADE NEEDED")
|
|
211
|
+
print(f" {reason}")
|
|
212
|
+
return 0
|
|
213
|
+
|
|
214
|
+
except Exception as e:
|
|
215
|
+
logger.exception("Failed to check routing recommendations")
|
|
216
|
+
print(f"❌ Error: {e}")
|
|
217
|
+
return 1
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def cmd_routing_models(args: Any) -> int:
|
|
221
|
+
"""Show model performance comparison.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
args: Arguments with provider, days
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
0 on success, 1 on error
|
|
228
|
+
"""
|
|
229
|
+
try:
|
|
230
|
+
# Get telemetry
|
|
231
|
+
tracker = UsageTracker.get_instance()
|
|
232
|
+
|
|
233
|
+
# Get recent entries
|
|
234
|
+
entries = tracker.get_recent_entries(limit=100000, days=args.days)
|
|
235
|
+
|
|
236
|
+
if args.provider:
|
|
237
|
+
entries = [e for e in entries if e.get("provider") == args.provider]
|
|
238
|
+
|
|
239
|
+
if not entries:
|
|
240
|
+
print(f"❌ No data found for provider '{args.provider}'")
|
|
241
|
+
return 1
|
|
242
|
+
|
|
243
|
+
# Group by model
|
|
244
|
+
by_model: dict[str, list] = {}
|
|
245
|
+
for entry in entries:
|
|
246
|
+
model = entry["model"]
|
|
247
|
+
if model not in by_model:
|
|
248
|
+
by_model[model] = []
|
|
249
|
+
by_model[model].append(entry)
|
|
250
|
+
|
|
251
|
+
print("\n" + "=" * 70)
|
|
252
|
+
print(f"MODEL PERFORMANCE COMPARISON - {args.provider.upper()}")
|
|
253
|
+
print(f"Last {args.days} days")
|
|
254
|
+
print("=" * 70)
|
|
255
|
+
|
|
256
|
+
# Sort by total calls
|
|
257
|
+
models_sorted = sorted(by_model.items(), key=lambda x: len(x[1]), reverse=True)
|
|
258
|
+
|
|
259
|
+
print(f"\n📊 {len(models_sorted)} model(s) used\n")
|
|
260
|
+
|
|
261
|
+
for model, model_entries in models_sorted:
|
|
262
|
+
total = len(model_entries)
|
|
263
|
+
successes = sum(1 for e in model_entries if e.get("success", True))
|
|
264
|
+
success_rate = successes / total
|
|
265
|
+
|
|
266
|
+
avg_cost = sum(e.get("cost", 0.0) for e in model_entries) / total
|
|
267
|
+
avg_latency = sum(e.get("duration_ms", 0) for e in model_entries) / total
|
|
268
|
+
|
|
269
|
+
# Quality score
|
|
270
|
+
quality_score = (success_rate * 100) - (avg_cost * 10)
|
|
271
|
+
|
|
272
|
+
print(f" {model}")
|
|
273
|
+
print(f" Calls: {total:,}")
|
|
274
|
+
print(f" Success rate: {success_rate:.1%}")
|
|
275
|
+
print(f" Avg cost: ${avg_cost:.4f}")
|
|
276
|
+
print(f" Avg latency: {avg_latency:.0f}ms")
|
|
277
|
+
print(f" Quality score: {quality_score:.2f}")
|
|
278
|
+
print()
|
|
279
|
+
|
|
280
|
+
return 0
|
|
281
|
+
|
|
282
|
+
except Exception as e:
|
|
283
|
+
logger.exception("Failed to get model performance")
|
|
284
|
+
print(f"❌ Error: {e}")
|
|
285
|
+
return 1
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""Setup commands for initialization and validation.
|
|
2
|
+
|
|
3
|
+
Copyright 2025 Smart-AI-Memory
|
|
4
|
+
Licensed under Fair Source License 0.9
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from attune.config import EmpathyConfig, _validate_file_path, load_config
|
|
10
|
+
from attune.logging_config import get_logger
|
|
11
|
+
|
|
12
|
+
logger = get_logger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def cmd_init(args):
|
|
16
|
+
"""Initialize a new Empathy Framework project.
|
|
17
|
+
|
|
18
|
+
Creates a configuration file with sensible defaults.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
args: Namespace object from argparse with attributes:
|
|
22
|
+
- format (str): Output format ('yaml' or 'json').
|
|
23
|
+
- output (str | None): Output file path.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
None: Creates configuration file at specified path.
|
|
27
|
+
|
|
28
|
+
Raises:
|
|
29
|
+
ValueError: If output path is invalid or unsafe.
|
|
30
|
+
"""
|
|
31
|
+
config_format = args.format
|
|
32
|
+
output_path = args.output or f"attune.config.{config_format}"
|
|
33
|
+
|
|
34
|
+
# Validate output path to prevent path traversal attacks
|
|
35
|
+
validated_path = _validate_file_path(output_path)
|
|
36
|
+
|
|
37
|
+
logger.info(f"Initializing new Empathy Framework project with format: {config_format}")
|
|
38
|
+
|
|
39
|
+
# Create default config
|
|
40
|
+
config = EmpathyConfig()
|
|
41
|
+
|
|
42
|
+
# Save to file
|
|
43
|
+
if config_format == "yaml":
|
|
44
|
+
config.to_yaml(str(validated_path))
|
|
45
|
+
logger.info(f"Created YAML configuration file: {output_path}")
|
|
46
|
+
logger.info(f"✓ Created YAML configuration: {output_path}")
|
|
47
|
+
elif config_format == "json":
|
|
48
|
+
config.to_json(str(validated_path))
|
|
49
|
+
logger.info(f"Created JSON configuration file: {validated_path}")
|
|
50
|
+
logger.info(f"✓ Created JSON configuration: {validated_path}")
|
|
51
|
+
|
|
52
|
+
logger.info("\nNext steps:")
|
|
53
|
+
logger.info(f" 1. Edit {output_path} to customize settings")
|
|
54
|
+
logger.info(" 2. Use 'empathy run' to start using the framework")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def cmd_validate(args):
|
|
58
|
+
"""Validate a configuration file.
|
|
59
|
+
|
|
60
|
+
Loads and validates the specified configuration file.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
args: Namespace object from argparse with attributes:
|
|
64
|
+
- config (str): Path to configuration file to validate.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
None: Prints validation result. Exits with code 1 on failure.
|
|
68
|
+
"""
|
|
69
|
+
filepath = args.config
|
|
70
|
+
logger.info(f"Validating configuration file: {filepath}")
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
config = load_config(filepath=filepath, use_env=False)
|
|
74
|
+
config.validate()
|
|
75
|
+
logger.info(f"Configuration validation successful: {filepath}")
|
|
76
|
+
logger.info(f"✓ Configuration valid: {filepath}")
|
|
77
|
+
logger.info(f"\n User ID: {config.user_id}")
|
|
78
|
+
logger.info(f" Target Level: {config.target_level}")
|
|
79
|
+
logger.info(f" Confidence Threshold: {config.confidence_threshold}")
|
|
80
|
+
logger.info(f" Persistence Backend: {config.persistence_backend}")
|
|
81
|
+
logger.info(f" Metrics Enabled: {config.metrics_enabled}")
|
|
82
|
+
except (OSError, FileNotFoundError) as e:
|
|
83
|
+
# Config file not found or cannot be read
|
|
84
|
+
logger.error(f"Configuration file error: {e}")
|
|
85
|
+
logger.error(f"✗ Cannot read configuration file: {e}")
|
|
86
|
+
sys.exit(1)
|
|
87
|
+
except ValueError as e:
|
|
88
|
+
# Invalid configuration values
|
|
89
|
+
logger.error(f"Configuration validation failed: {e}")
|
|
90
|
+
logger.error(f"✗ Configuration invalid: {e}")
|
|
91
|
+
sys.exit(1)
|
|
92
|
+
except Exception as e:
|
|
93
|
+
# Unexpected errors during config validation
|
|
94
|
+
logger.exception(f"Unexpected error validating configuration: {e}")
|
|
95
|
+
logger.error(f"✗ Configuration invalid: {e}")
|
|
96
|
+
sys.exit(1)
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"""Status and health check commands for the CLI.
|
|
2
|
+
|
|
3
|
+
Copyright 2025 Smart-AI-Memory
|
|
4
|
+
Licensed under Fair Source License 0.9
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def cmd_status(args):
|
|
11
|
+
"""Session status assistant - prioritized project status report.
|
|
12
|
+
|
|
13
|
+
Collects and displays project status including patterns, git context,
|
|
14
|
+
and health metrics with priority scoring.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
args: Namespace object from argparse with attributes:
|
|
18
|
+
- patterns_dir (str): Path to patterns directory (default: ./patterns).
|
|
19
|
+
- project_root (str): Project root directory (default: .).
|
|
20
|
+
- inactivity (int): Minutes of inactivity before showing status.
|
|
21
|
+
- full (bool): If True, show all items without limit.
|
|
22
|
+
- json (bool): If True, output as JSON format.
|
|
23
|
+
- select (int | None): Select specific item for action prompt.
|
|
24
|
+
- force (bool): If True, show status even with recent activity.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
None: Prints prioritized status report or JSON output.
|
|
28
|
+
"""
|
|
29
|
+
from attune_llm.session_status import SessionStatusCollector
|
|
30
|
+
|
|
31
|
+
config = {"inactivity_minutes": args.inactivity}
|
|
32
|
+
collector = SessionStatusCollector(
|
|
33
|
+
patterns_dir=args.patterns_dir,
|
|
34
|
+
project_root=args.project_root,
|
|
35
|
+
config=config,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# Check if should show (unless forced)
|
|
39
|
+
if not args.force and not collector.should_show():
|
|
40
|
+
print("No status update needed (recent activity detected).")
|
|
41
|
+
print("Use --force to show status anyway.")
|
|
42
|
+
return
|
|
43
|
+
|
|
44
|
+
# Collect status
|
|
45
|
+
status = collector.collect()
|
|
46
|
+
|
|
47
|
+
# Handle selection
|
|
48
|
+
if args.select:
|
|
49
|
+
prompt = collector.get_action_prompt(status, args.select)
|
|
50
|
+
if prompt:
|
|
51
|
+
print(f"\nAction prompt for selection {args.select}:\n")
|
|
52
|
+
print(prompt)
|
|
53
|
+
else:
|
|
54
|
+
print(f"Invalid selection: {args.select}")
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
# Output
|
|
58
|
+
if args.json:
|
|
59
|
+
print(collector.format_json(status))
|
|
60
|
+
else:
|
|
61
|
+
max_items = None if args.full else 5
|
|
62
|
+
print()
|
|
63
|
+
print(collector.format_output(status, max_items=max_items))
|
|
64
|
+
print()
|
|
65
|
+
|
|
66
|
+
# Record interaction
|
|
67
|
+
collector.record_interaction()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def cmd_review(args):
|
|
71
|
+
"""Pattern-based code review against historical bugs.
|
|
72
|
+
|
|
73
|
+
Note: This command has been deprecated. The underlying workflow module
|
|
74
|
+
has been removed. Use 'empathy workflow run bug-predict' instead.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
args: Namespace object from argparse.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
None: Prints deprecation message.
|
|
81
|
+
"""
|
|
82
|
+
print("⚠️ The 'review' command has been deprecated.")
|
|
83
|
+
print()
|
|
84
|
+
print("The CodeReviewWorkflow module has been removed.")
|
|
85
|
+
print("Please use one of these alternatives:")
|
|
86
|
+
print()
|
|
87
|
+
print(" empathy workflow run bug-predict # Scan for risky patterns")
|
|
88
|
+
print(" ruff check <files> # Fast linting")
|
|
89
|
+
print(" bandit -r <path> # Security scanning")
|
|
90
|
+
print()
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def cmd_health(args):
|
|
94
|
+
"""Code health assistant - run health checks and auto-fix issues.
|
|
95
|
+
|
|
96
|
+
Runs comprehensive health checks including linting, type checking,
|
|
97
|
+
and formatting with optional auto-fix capability.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
args: Namespace object from argparse with attributes:
|
|
101
|
+
- check (str | None): Specific check to run (lint/type/format/test).
|
|
102
|
+
- deep (bool): If True, run comprehensive checks.
|
|
103
|
+
- fix (bool): If True, auto-fix issues where possible.
|
|
104
|
+
- threshold (str): Severity threshold for issues.
|
|
105
|
+
- project_root (str): Project root directory.
|
|
106
|
+
- patterns_dir (str): Path to patterns directory.
|
|
107
|
+
- details (bool): If True, show detailed issue list.
|
|
108
|
+
- compare (str | None): Compare against historical baseline.
|
|
109
|
+
- export (str | None): Export results to file.
|
|
110
|
+
- json (bool): If True, output as JSON format.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
None: Prints health check results and optionally fixes issues.
|
|
114
|
+
"""
|
|
115
|
+
from attune_llm.code_health import (
|
|
116
|
+
AutoFixer,
|
|
117
|
+
CheckCategory,
|
|
118
|
+
HealthCheckRunner,
|
|
119
|
+
HealthTrendTracker,
|
|
120
|
+
format_health_output,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
runner = HealthCheckRunner(
|
|
124
|
+
project_root=args.project_root,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# Determine what checks to run
|
|
128
|
+
if args.check:
|
|
129
|
+
# Run specific check
|
|
130
|
+
try:
|
|
131
|
+
category = CheckCategory(args.check)
|
|
132
|
+
report_future = runner.run_check(category)
|
|
133
|
+
result = asyncio.run(report_future)
|
|
134
|
+
# Create a minimal report with just this result
|
|
135
|
+
from attune_llm.code_health import HealthReport
|
|
136
|
+
|
|
137
|
+
report = HealthReport(project_root=args.project_root)
|
|
138
|
+
report.add_result(result)
|
|
139
|
+
except ValueError:
|
|
140
|
+
print(f"Unknown check category: {args.check}")
|
|
141
|
+
print(f"Available: {', '.join(c.value for c in CheckCategory)}")
|
|
142
|
+
return
|
|
143
|
+
elif args.deep:
|
|
144
|
+
# Run all checks
|
|
145
|
+
print("Running comprehensive health check...\n")
|
|
146
|
+
report = asyncio.run(runner.run_all())
|
|
147
|
+
else:
|
|
148
|
+
# Run quick checks (default)
|
|
149
|
+
report = asyncio.run(runner.run_quick())
|
|
150
|
+
|
|
151
|
+
# Handle fix mode
|
|
152
|
+
if args.fix:
|
|
153
|
+
fixer = AutoFixer()
|
|
154
|
+
|
|
155
|
+
if args.dry_run:
|
|
156
|
+
# Preview only
|
|
157
|
+
fixes = fixer.preview_fixes(report)
|
|
158
|
+
if fixes:
|
|
159
|
+
print("Would fix the following issues:\n")
|
|
160
|
+
for fix in fixes:
|
|
161
|
+
safe_indicator = " (safe)" if fix["safe"] else " (needs confirmation)"
|
|
162
|
+
print(f" [{fix['category']}] {fix['file']}")
|
|
163
|
+
print(f" {fix['issue']}")
|
|
164
|
+
print(f" Command: {fix['fix_command']}{safe_indicator}")
|
|
165
|
+
print()
|
|
166
|
+
else:
|
|
167
|
+
print("No auto-fixable issues found.")
|
|
168
|
+
return
|
|
169
|
+
|
|
170
|
+
# Apply fixes
|
|
171
|
+
if args.check:
|
|
172
|
+
try:
|
|
173
|
+
category = CheckCategory(args.check)
|
|
174
|
+
result = asyncio.run(fixer.fix_category(report, category))
|
|
175
|
+
except ValueError:
|
|
176
|
+
result = {"fixed": [], "skipped": [], "failed": []}
|
|
177
|
+
else:
|
|
178
|
+
result = asyncio.run(fixer.fix_all(report, interactive=args.interactive))
|
|
179
|
+
|
|
180
|
+
# Report fix results
|
|
181
|
+
if result["fixed"]:
|
|
182
|
+
print(f"✓ Fixed {len(result['fixed'])} issue(s)")
|
|
183
|
+
for fix in result["fixed"][:5]:
|
|
184
|
+
print(f" - {fix['file_path']}: {fix['message']}")
|
|
185
|
+
if len(result["fixed"]) > 5:
|
|
186
|
+
print(f" ... and {len(result['fixed']) - 5} more")
|
|
187
|
+
|
|
188
|
+
if result["skipped"]:
|
|
189
|
+
if args.interactive:
|
|
190
|
+
print(f"\n⚠ Skipped {len(result['skipped'])} issue(s) (could not auto-fix)")
|
|
191
|
+
else:
|
|
192
|
+
print(
|
|
193
|
+
f"\n⚠ Skipped {len(result['skipped'])} issue(s) (use --interactive to review)",
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
if result["failed"]:
|
|
197
|
+
print(f"\n✗ Failed to fix {len(result['failed'])} issue(s)")
|
|
198
|
+
|
|
199
|
+
return
|
|
200
|
+
|
|
201
|
+
# Handle trends
|
|
202
|
+
if args.trends:
|
|
203
|
+
tracker = HealthTrendTracker(project_root=args.project_root)
|
|
204
|
+
trends = tracker.get_trends(days=args.trends)
|
|
205
|
+
|
|
206
|
+
print(f"📈 Health Trends ({trends['period_days']} days)\n")
|
|
207
|
+
print(f"Average Score: {trends['average_score']}/100")
|
|
208
|
+
print(f"Trend: {trends['trend_direction']} ({trends['score_change']:+d})")
|
|
209
|
+
|
|
210
|
+
if trends["data_points"]:
|
|
211
|
+
print("\nRecent scores:")
|
|
212
|
+
for point in trends["data_points"][:7]:
|
|
213
|
+
print(f" {point['date']}: {point['score']}/100")
|
|
214
|
+
|
|
215
|
+
hotspots = tracker.identify_hotspots()
|
|
216
|
+
if hotspots:
|
|
217
|
+
print("\n🔥 Hotspots (files with recurring issues):")
|
|
218
|
+
for spot in hotspots[:5]:
|
|
219
|
+
print(f" {spot['file']}: {spot['issue_count']} issues")
|
|
220
|
+
|
|
221
|
+
return
|
|
222
|
+
|
|
223
|
+
# Output report
|
|
224
|
+
if args.json:
|
|
225
|
+
import json
|
|
226
|
+
|
|
227
|
+
print(json.dumps(report.to_dict(), indent=2, default=str))
|
|
228
|
+
else:
|
|
229
|
+
level = 3 if args.full else (2 if args.details else 1)
|
|
230
|
+
print(format_health_output(report, level=level))
|
|
231
|
+
|
|
232
|
+
# Record to trend history
|
|
233
|
+
if not args.check: # Only record full runs
|
|
234
|
+
tracker = HealthTrendTracker(project_root=args.project_root)
|
|
235
|
+
tracker.record_check(report)
|