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
attune/platform_utils.py
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"""Cross-Platform Utilities for Empathy Framework
|
|
2
|
+
|
|
3
|
+
Provides platform-independent utilities for:
|
|
4
|
+
- File paths and directories
|
|
5
|
+
- File encoding
|
|
6
|
+
- Asyncio event loop handling
|
|
7
|
+
- Environment detection
|
|
8
|
+
|
|
9
|
+
Copyright 2025 Smart-AI-Memory
|
|
10
|
+
Licensed under Fair Source License 0.9
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import asyncio
|
|
14
|
+
import os
|
|
15
|
+
import platform
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
from attune.config import _validate_file_path
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def is_windows() -> bool:
|
|
23
|
+
"""Check if running on Windows."""
|
|
24
|
+
return platform.system() == "Windows"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def is_macos() -> bool:
|
|
28
|
+
"""Check if running on macOS."""
|
|
29
|
+
return platform.system() == "Darwin"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def is_linux() -> bool:
|
|
33
|
+
"""Check if running on Linux."""
|
|
34
|
+
return platform.system() == "Linux"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_default_log_dir() -> Path:
|
|
38
|
+
"""Get the default log directory for the current platform.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Path: Platform-appropriate log directory
|
|
42
|
+
- Windows: %APPDATA%/empathy/logs
|
|
43
|
+
- macOS: ~/Library/Logs/empathy
|
|
44
|
+
- Linux: /var/log/empathy (if writable) or ~/.local/share/empathy/logs
|
|
45
|
+
|
|
46
|
+
"""
|
|
47
|
+
if is_windows():
|
|
48
|
+
appdata = os.environ.get("APPDATA", os.path.expanduser("~"))
|
|
49
|
+
return Path(appdata) / "empathy" / "logs"
|
|
50
|
+
if is_macos():
|
|
51
|
+
return Path.home() / "Library" / "Logs" / "empathy"
|
|
52
|
+
# Linux and other Unix
|
|
53
|
+
var_log = Path("/var/log/empathy")
|
|
54
|
+
if var_log.exists() or (var_log.parent.exists() and os.access(var_log.parent, os.W_OK)):
|
|
55
|
+
return var_log
|
|
56
|
+
# Fallback to user directory
|
|
57
|
+
return Path.home() / ".local" / "share" / "empathy" / "logs"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_default_data_dir() -> Path:
|
|
61
|
+
"""Get the default data directory for the current platform.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
Path: Platform-appropriate data directory
|
|
65
|
+
- Windows: %APPDATA%/empathy
|
|
66
|
+
- macOS: ~/Library/Application Support/empathy
|
|
67
|
+
- Linux: ~/.local/share/empathy
|
|
68
|
+
|
|
69
|
+
"""
|
|
70
|
+
if is_windows():
|
|
71
|
+
appdata = os.environ.get("APPDATA", os.path.expanduser("~"))
|
|
72
|
+
return Path(appdata) / "empathy"
|
|
73
|
+
if is_macos():
|
|
74
|
+
return Path.home() / "Library" / "Application Support" / "empathy"
|
|
75
|
+
# Linux and other Unix
|
|
76
|
+
xdg_data = os.environ.get("XDG_DATA_HOME", str(Path.home() / ".local" / "share"))
|
|
77
|
+
return Path(xdg_data) / "empathy"
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def get_default_config_dir() -> Path:
|
|
81
|
+
"""Get the default configuration directory for the current platform.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Path: Platform-appropriate config directory
|
|
85
|
+
- Windows: %APPDATA%/empathy
|
|
86
|
+
- macOS: ~/Library/Preferences/empathy
|
|
87
|
+
- Linux: ~/.config/empathy
|
|
88
|
+
|
|
89
|
+
"""
|
|
90
|
+
if is_windows():
|
|
91
|
+
appdata = os.environ.get("APPDATA", os.path.expanduser("~"))
|
|
92
|
+
return Path(appdata) / "empathy"
|
|
93
|
+
if is_macos():
|
|
94
|
+
return Path.home() / "Library" / "Preferences" / "empathy"
|
|
95
|
+
# Linux and other Unix
|
|
96
|
+
xdg_config = os.environ.get("XDG_CONFIG_HOME", str(Path.home() / ".config"))
|
|
97
|
+
return Path(xdg_config) / "empathy"
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def get_default_cache_dir() -> Path:
|
|
101
|
+
"""Get the default cache directory for the current platform.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Path: Platform-appropriate cache directory
|
|
105
|
+
- Windows: %LOCALAPPDATA%/empathy/cache
|
|
106
|
+
- macOS: ~/Library/Caches/empathy
|
|
107
|
+
- Linux: ~/.cache/empathy
|
|
108
|
+
|
|
109
|
+
"""
|
|
110
|
+
if is_windows():
|
|
111
|
+
localappdata = os.environ.get(
|
|
112
|
+
"LOCALAPPDATA",
|
|
113
|
+
os.environ.get("APPDATA", os.path.expanduser("~")),
|
|
114
|
+
)
|
|
115
|
+
return Path(localappdata) / "empathy" / "cache"
|
|
116
|
+
if is_macos():
|
|
117
|
+
return Path.home() / "Library" / "Caches" / "empathy"
|
|
118
|
+
# Linux and other Unix
|
|
119
|
+
xdg_cache = os.environ.get("XDG_CACHE_HOME", str(Path.home() / ".cache"))
|
|
120
|
+
return Path(xdg_cache) / "empathy"
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def setup_asyncio_policy() -> None:
|
|
124
|
+
"""Configure asyncio event loop policy for the current platform.
|
|
125
|
+
|
|
126
|
+
On Windows, this uses WindowsSelectorEventLoopPolicy to avoid issues
|
|
127
|
+
with the default ProactorEventLoop, particularly with subprocesses
|
|
128
|
+
and certain network operations.
|
|
129
|
+
|
|
130
|
+
This should be called early in the application startup, before
|
|
131
|
+
any asyncio.run() calls.
|
|
132
|
+
"""
|
|
133
|
+
if is_windows():
|
|
134
|
+
# Windows requires WindowsSelectorEventLoopPolicy for compatibility
|
|
135
|
+
# with many libraries and subprocess operations
|
|
136
|
+
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) # type: ignore[attr-defined]
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def safe_run_async(coro: Any, debug: bool = False) -> Any:
|
|
140
|
+
"""Run an async coroutine with platform-appropriate event loop handling.
|
|
141
|
+
|
|
142
|
+
This is a cross-platform wrapper for asyncio.run() that handles
|
|
143
|
+
Windows-specific event loop requirements.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
coro: Coroutine to run
|
|
147
|
+
debug: Enable asyncio debug mode
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
Result of the coroutine
|
|
151
|
+
|
|
152
|
+
"""
|
|
153
|
+
setup_asyncio_policy()
|
|
154
|
+
return asyncio.run(coro, debug=debug)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def open_text_file(path: str | Path, mode: str = "r", **kwargs: Any):
|
|
158
|
+
"""Open a text file with UTF-8 encoding by default.
|
|
159
|
+
|
|
160
|
+
This ensures consistent encoding across platforms, as Windows
|
|
161
|
+
defaults to cp1252 while Unix defaults to UTF-8.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
path: File path to open
|
|
165
|
+
mode: File mode (r, w, a, etc.)
|
|
166
|
+
**kwargs: Additional arguments passed to open()
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
File object
|
|
170
|
+
|
|
171
|
+
"""
|
|
172
|
+
kwargs.setdefault("encoding", "utf-8")
|
|
173
|
+
return open(path, mode, **kwargs)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def read_text_file(path: str | Path, encoding: str = "utf-8") -> str:
|
|
177
|
+
"""Read a text file with UTF-8 encoding by default.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
path: File path to read
|
|
181
|
+
encoding: File encoding (default: utf-8)
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
File contents as string
|
|
185
|
+
|
|
186
|
+
"""
|
|
187
|
+
return Path(path).read_text(encoding=encoding)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def write_text_file(path: str | Path, content: str, encoding: str = "utf-8") -> int:
|
|
191
|
+
"""Write content to a text file with UTF-8 encoding by default.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
path: File path to write
|
|
195
|
+
content: Content to write
|
|
196
|
+
encoding: File encoding (default: utf-8)
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
Number of characters written
|
|
200
|
+
|
|
201
|
+
"""
|
|
202
|
+
validated_path = _validate_file_path(str(path))
|
|
203
|
+
result: int = validated_path.write_text(content, encoding=encoding)
|
|
204
|
+
return result
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def normalize_path(path: str | Path) -> Path:
|
|
208
|
+
"""Normalize a path for the current platform.
|
|
209
|
+
|
|
210
|
+
Converts forward slashes to backslashes on Windows and
|
|
211
|
+
resolves any relative path components.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
path: Path to normalize
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
Normalized Path object
|
|
218
|
+
|
|
219
|
+
"""
|
|
220
|
+
return Path(path).resolve()
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def get_temp_dir() -> Path:
|
|
224
|
+
"""Get the system temporary directory.
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
Path to the system temp directory
|
|
228
|
+
|
|
229
|
+
"""
|
|
230
|
+
import tempfile
|
|
231
|
+
|
|
232
|
+
return Path(tempfile.gettempdir())
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def ensure_dir(path: str | Path) -> Path:
|
|
236
|
+
"""Ensure a directory exists, creating it if necessary.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
path: Directory path to ensure
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
Path object for the directory
|
|
243
|
+
|
|
244
|
+
"""
|
|
245
|
+
dir_path = Path(path)
|
|
246
|
+
dir_path.mkdir(parents=True, exist_ok=True)
|
|
247
|
+
return dir_path
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
# Platform information for diagnostics
|
|
251
|
+
PLATFORM_INFO = {
|
|
252
|
+
"system": platform.system(),
|
|
253
|
+
"release": platform.release(),
|
|
254
|
+
"version": platform.version(),
|
|
255
|
+
"machine": platform.machine(),
|
|
256
|
+
"python_version": platform.python_version(),
|
|
257
|
+
"is_windows": is_windows(),
|
|
258
|
+
"is_macos": is_macos(),
|
|
259
|
+
"is_linux": is_linux(),
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def get_platform_info() -> dict:
|
|
264
|
+
"""Get platform information for diagnostics."""
|
|
265
|
+
return PLATFORM_INFO.copy()
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Empathy Framework - Plugin System
|
|
2
|
+
|
|
3
|
+
Enables modular extension of the Empathy Framework with domain-specific plugins.
|
|
4
|
+
|
|
5
|
+
Copyright 2025 Smart AI Memory, LLC
|
|
6
|
+
Licensed under Fair Source 0.9
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from .base import (
|
|
10
|
+
BasePlugin,
|
|
11
|
+
BaseWorkflow,
|
|
12
|
+
PluginError,
|
|
13
|
+
PluginLoadError,
|
|
14
|
+
PluginMetadata,
|
|
15
|
+
PluginValidationError,
|
|
16
|
+
)
|
|
17
|
+
from .registry import PluginRegistry, get_global_registry
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"BasePlugin",
|
|
21
|
+
"BaseWorkflow",
|
|
22
|
+
"PluginError",
|
|
23
|
+
"PluginLoadError",
|
|
24
|
+
"PluginMetadata",
|
|
25
|
+
"PluginRegistry",
|
|
26
|
+
"PluginValidationError",
|
|
27
|
+
"get_global_registry",
|
|
28
|
+
]
|
attune/plugins/base.py
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
"""Empathy Framework - Plugin System Base Classes
|
|
2
|
+
|
|
3
|
+
This module provides the core abstractions for creating domain-specific plugins
|
|
4
|
+
that extend the Empathy Framework.
|
|
5
|
+
|
|
6
|
+
Copyright 2025 Smart AI Memory, LLC
|
|
7
|
+
Licensed under Fair Source 0.9
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from abc import ABC, abstractmethod
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class PluginMetadata:
|
|
21
|
+
"""Metadata about a plugin"""
|
|
22
|
+
|
|
23
|
+
name: str
|
|
24
|
+
version: str
|
|
25
|
+
domain: str
|
|
26
|
+
description: str
|
|
27
|
+
author: str
|
|
28
|
+
license: str
|
|
29
|
+
requires_core_version: str # Minimum core framework version
|
|
30
|
+
dependencies: list[str] | None = None # Additional package dependencies
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class BaseWorkflow(ABC):
|
|
34
|
+
"""Universal base class for all workflows across all domains.
|
|
35
|
+
|
|
36
|
+
This replaces domain-specific base classes (BaseCoachWorkflow, etc.)
|
|
37
|
+
to provide a unified interface.
|
|
38
|
+
|
|
39
|
+
Design Philosophy:
|
|
40
|
+
- Domain-agnostic: Works for software, healthcare, finance, etc.
|
|
41
|
+
- Level-aware: Each workflow declares its empathy level
|
|
42
|
+
- Pattern-contributing: Workflows share learnings via pattern library
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(self, name: str, domain: str, empathy_level: int, category: str | None = None):
|
|
46
|
+
"""Initialize a workflow
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
name: Human-readable workflow name
|
|
50
|
+
domain: Domain this workflow belongs to (e.g., 'software', 'healthcare')
|
|
51
|
+
empathy_level: Which empathy level this workflow operates at (1-5)
|
|
52
|
+
category: Optional category within domain
|
|
53
|
+
|
|
54
|
+
"""
|
|
55
|
+
self.name = name
|
|
56
|
+
self.domain = domain
|
|
57
|
+
self.empathy_level = empathy_level
|
|
58
|
+
self.category = category
|
|
59
|
+
self.logger = logging.getLogger(f"workflow.{domain}.{name}")
|
|
60
|
+
|
|
61
|
+
@abstractmethod
|
|
62
|
+
async def analyze(self, context: dict[str, Any]) -> dict[str, Any]:
|
|
63
|
+
"""Analyze the given context and return results.
|
|
64
|
+
|
|
65
|
+
This is the main entry point for all workflows. The context structure
|
|
66
|
+
is domain-specific but the return format should follow a standard pattern.
|
|
67
|
+
Subclasses must implement domain-specific analysis logic that aligns with
|
|
68
|
+
the workflow's empathy level.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
context: dict[str, Any]
|
|
72
|
+
Domain-specific context dictionary. Must contain all fields returned
|
|
73
|
+
by get_required_context(). Examples:
|
|
74
|
+
- Software: {'code': str, 'file_path': str, 'language': str}
|
|
75
|
+
- Healthcare: {'patient_id': str, 'vitals': dict, 'medications': list}
|
|
76
|
+
- Finance: {'transactions': list, 'account': dict, 'period': str}
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
dict[str, Any]
|
|
80
|
+
Analysis results dictionary containing:
|
|
81
|
+
- 'issues': list[dict] - Current issues found (Levels 1-3 analysis)
|
|
82
|
+
- 'predictions': list[dict] - Future issues predicted (Level 4 analysis)
|
|
83
|
+
- 'recommendations': list[dict] - Actionable next steps
|
|
84
|
+
- 'patterns': list[str] - Patterns detected for the pattern library
|
|
85
|
+
- 'confidence': float - Confidence score between 0.0 and 1.0
|
|
86
|
+
- 'workflow': str - Name of the workflow that performed analysis
|
|
87
|
+
- 'empathy_level': int - Empathy level of this analysis (1-5)
|
|
88
|
+
- 'timestamp': str - ISO format timestamp of analysis
|
|
89
|
+
|
|
90
|
+
Raises:
|
|
91
|
+
ValueError: If context is invalid or missing required fields
|
|
92
|
+
RuntimeError: If analysis fails due to domain-specific errors
|
|
93
|
+
TimeoutError: If analysis takes too long to complete
|
|
94
|
+
|
|
95
|
+
Note:
|
|
96
|
+
- Validate context using self.validate_context() at the beginning
|
|
97
|
+
- Each issue/prediction should include 'severity', 'description', 'affected_component'
|
|
98
|
+
- Use self.contribute_patterns() to extract learnings for the pattern library
|
|
99
|
+
- Confidence scores should reflect uncertainty in the analysis
|
|
100
|
+
- This method is async to support long-running analyses
|
|
101
|
+
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
@abstractmethod
|
|
105
|
+
def get_required_context(self) -> list[str]:
|
|
106
|
+
"""Declare what context fields this workflow needs.
|
|
107
|
+
|
|
108
|
+
This method defines the contract between the caller and the workflow.
|
|
109
|
+
The caller must provide all declared fields before calling analyze().
|
|
110
|
+
This enables validation via validate_context() and helps with introspection.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
list[str]
|
|
114
|
+
List of required context field names (keys). Each string should be
|
|
115
|
+
a simple identifier that matches the keys in context dictionaries
|
|
116
|
+
passed to analyze().
|
|
117
|
+
|
|
118
|
+
Examples:
|
|
119
|
+
Software workflow returns: ['code', 'file_path', 'language']
|
|
120
|
+
Healthcare workflow returns: ['patient_id', 'vitals', 'medications']
|
|
121
|
+
Finance workflow returns: ['transactions', 'account_id', 'period']
|
|
122
|
+
|
|
123
|
+
Note:
|
|
124
|
+
- Must return at least one field (even if minimal)
|
|
125
|
+
- Field names should match exactly what analyze() expects in context
|
|
126
|
+
- Order is not significant but consistency helps with documentation
|
|
127
|
+
- Consider validation requirements when declaring fields
|
|
128
|
+
- Used by validate_context() to check required fields before analyze()
|
|
129
|
+
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
def validate_context(self, context: dict[str, Any]) -> bool:
|
|
133
|
+
"""Validate that context contains required fields.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
context: Context to validate
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
True if valid, raises ValueError if invalid
|
|
140
|
+
|
|
141
|
+
"""
|
|
142
|
+
required = self.get_required_context()
|
|
143
|
+
missing = [key for key in required if key not in context]
|
|
144
|
+
|
|
145
|
+
if missing:
|
|
146
|
+
raise ValueError(f"Workflow '{self.name}' missing required context: {missing}")
|
|
147
|
+
|
|
148
|
+
return True
|
|
149
|
+
|
|
150
|
+
def get_empathy_level(self) -> int:
|
|
151
|
+
"""Get the empathy level this workflow operates at"""
|
|
152
|
+
return self.empathy_level
|
|
153
|
+
|
|
154
|
+
def contribute_patterns(self, analysis_result: dict[str, Any]) -> dict[str, Any]:
|
|
155
|
+
"""Extract patterns from analysis for the shared pattern library.
|
|
156
|
+
|
|
157
|
+
This enables cross-domain learning (Level 5 Systems Empathy).
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
analysis_result: Result from analyze()
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
Dictionary of patterns in standard format
|
|
164
|
+
|
|
165
|
+
"""
|
|
166
|
+
# Default implementation - override for custom pattern extraction
|
|
167
|
+
return {
|
|
168
|
+
"workflow": self.name,
|
|
169
|
+
"domain": self.domain,
|
|
170
|
+
"timestamp": datetime.now().isoformat(),
|
|
171
|
+
"patterns": analysis_result.get("patterns", []),
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class BasePlugin(ABC):
|
|
176
|
+
"""Base class for domain plugins.
|
|
177
|
+
|
|
178
|
+
A plugin is a collection of workflows and patterns for a specific domain.
|
|
179
|
+
|
|
180
|
+
Example:
|
|
181
|
+
- SoftwarePlugin: 16+ coach workflows for code analysis
|
|
182
|
+
- HealthcarePlugin: Clinical and compliance workflows
|
|
183
|
+
- FinancePlugin: Fraud detection, compliance workflows
|
|
184
|
+
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
def __init__(self):
|
|
188
|
+
self.logger = logging.getLogger(f"plugin.{self.get_metadata().domain}")
|
|
189
|
+
self._workflows: dict[str, type[BaseWorkflow]] = {}
|
|
190
|
+
self._initialized = False
|
|
191
|
+
|
|
192
|
+
@abstractmethod
|
|
193
|
+
def get_metadata(self) -> PluginMetadata:
|
|
194
|
+
"""Return metadata about this plugin.
|
|
195
|
+
|
|
196
|
+
This method provides essential information about the plugin that the
|
|
197
|
+
framework uses for loading, validation, and discovery. It must return
|
|
198
|
+
consistent metadata across multiple calls.
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
PluginMetadata
|
|
202
|
+
A PluginMetadata instance containing:
|
|
203
|
+
- name: str - Human-readable plugin name (e.g., 'Software Plugin')
|
|
204
|
+
- version: str - Semantic version string (e.g., '1.0.0')
|
|
205
|
+
- domain: str - Domain this plugin serves (e.g., 'software', 'healthcare')
|
|
206
|
+
- description: str - Brief description of plugin functionality
|
|
207
|
+
- author: str - Plugin author or organization name
|
|
208
|
+
- license: str - License identifier (e.g., 'Apache-2.0', 'MIT')
|
|
209
|
+
- requires_core_version: str - Minimum core framework version (e.g., '1.0.0')
|
|
210
|
+
- dependencies: list[str] - Optional list of required packages
|
|
211
|
+
|
|
212
|
+
Note:
|
|
213
|
+
- Called during plugin initialization to validate compatibility
|
|
214
|
+
- Used for plugin discovery and listing
|
|
215
|
+
- Should be immutable (return same values each call)
|
|
216
|
+
- Version should follow semantic versioning
|
|
217
|
+
- Domain names should be lowercase and consistent across plugins
|
|
218
|
+
- Core version requirement ensures framework compatibility
|
|
219
|
+
|
|
220
|
+
"""
|
|
221
|
+
|
|
222
|
+
@abstractmethod
|
|
223
|
+
def register_workflows(self) -> dict[str, type[BaseWorkflow]]:
|
|
224
|
+
"""Register all workflows provided by this plugin.
|
|
225
|
+
|
|
226
|
+
This method defines all analysis workflows available in this plugin.
|
|
227
|
+
Workflows are lazy-instantiated by get_workflow() when first requested.
|
|
228
|
+
This method is called during plugin initialization.
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
dict[str, type[BaseWorkflow]]
|
|
232
|
+
Dictionary mapping workflow identifiers to Workflow classes (not instances).
|
|
233
|
+
Keys should be lowercase, snake_case identifiers. Values should be
|
|
234
|
+
uninstantiated class references.
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
dict[str, type[BaseWorkflow]]
|
|
238
|
+
Mapping structure:
|
|
239
|
+
{
|
|
240
|
+
'workflow_id': WorkflowClass,
|
|
241
|
+
'another_workflow': AnotherWorkflowClass,
|
|
242
|
+
...
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
Example:
|
|
246
|
+
Software plugin might return:
|
|
247
|
+
{
|
|
248
|
+
'security': SecurityWorkflow,
|
|
249
|
+
'performance': PerformanceWorkflow,
|
|
250
|
+
'maintainability': MaintainabilityWorkflow,
|
|
251
|
+
'accessibility': AccessibilityWorkflow,
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
Note:
|
|
255
|
+
- Return only the class, not instances (instantiation is lazy)
|
|
256
|
+
- Use consistent, descriptive workflow IDs
|
|
257
|
+
- All returned classes must be subclasses of BaseWorkflow
|
|
258
|
+
- Can return empty dict {} if plugin provides no workflows initially
|
|
259
|
+
- Called once during initialization via initialize()
|
|
260
|
+
- Framework caches results in self._workflows
|
|
261
|
+
|
|
262
|
+
"""
|
|
263
|
+
|
|
264
|
+
def register_patterns(self) -> dict[str, Any]:
|
|
265
|
+
"""Register domain-specific patterns for the pattern library.
|
|
266
|
+
|
|
267
|
+
Returns:
|
|
268
|
+
Dictionary of patterns in standard format
|
|
269
|
+
|
|
270
|
+
"""
|
|
271
|
+
# Optional - override if plugin provides pre-built patterns
|
|
272
|
+
return {}
|
|
273
|
+
|
|
274
|
+
def initialize(self) -> None:
|
|
275
|
+
"""Initialize the plugin (lazy initialization).
|
|
276
|
+
|
|
277
|
+
Called once before first use. Override to perform setup:
|
|
278
|
+
- Load configuration
|
|
279
|
+
- Initialize domain-specific services
|
|
280
|
+
- Validate dependencies
|
|
281
|
+
"""
|
|
282
|
+
if self._initialized:
|
|
283
|
+
return
|
|
284
|
+
|
|
285
|
+
self.logger.info(f"Initializing plugin: {self.get_metadata().name}")
|
|
286
|
+
|
|
287
|
+
# Register workflows
|
|
288
|
+
self._workflows = self.register_workflows()
|
|
289
|
+
|
|
290
|
+
self.logger.info(
|
|
291
|
+
f"Plugin '{self.get_metadata().name}' initialized with {len(self._workflows)} workflows",
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
self._initialized = True
|
|
295
|
+
|
|
296
|
+
def get_workflow(self, workflow_id: str) -> type[BaseWorkflow] | None:
|
|
297
|
+
"""Get a workflow by ID.
|
|
298
|
+
|
|
299
|
+
Args:
|
|
300
|
+
workflow_id: Workflow identifier
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
Workflow class or None if not found
|
|
304
|
+
|
|
305
|
+
"""
|
|
306
|
+
if not self._initialized:
|
|
307
|
+
self.initialize()
|
|
308
|
+
|
|
309
|
+
return self._workflows.get(workflow_id)
|
|
310
|
+
|
|
311
|
+
def list_workflows(self) -> list[str]:
|
|
312
|
+
"""List all workflow IDs provided by this plugin.
|
|
313
|
+
|
|
314
|
+
Returns:
|
|
315
|
+
List of workflow identifiers
|
|
316
|
+
|
|
317
|
+
"""
|
|
318
|
+
if not self._initialized:
|
|
319
|
+
self.initialize()
|
|
320
|
+
|
|
321
|
+
return list(self._workflows.keys())
|
|
322
|
+
|
|
323
|
+
def get_workflow_info(self, workflow_id: str) -> dict[str, Any] | None:
|
|
324
|
+
"""Get information about a workflow without instantiating it.
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
workflow_id: Workflow identifier
|
|
328
|
+
|
|
329
|
+
Returns:
|
|
330
|
+
Dictionary with workflow metadata
|
|
331
|
+
|
|
332
|
+
"""
|
|
333
|
+
workflow_class = self.get_workflow(workflow_id)
|
|
334
|
+
if not workflow_class:
|
|
335
|
+
return None
|
|
336
|
+
|
|
337
|
+
# Create temporary instance to get metadata
|
|
338
|
+
# (workflows should be lightweight to construct)
|
|
339
|
+
# Subclasses provide their own defaults for name, domain, empathy_level
|
|
340
|
+
temp_instance = workflow_class() # type: ignore[call-arg]
|
|
341
|
+
|
|
342
|
+
return {
|
|
343
|
+
"id": workflow_id,
|
|
344
|
+
"name": temp_instance.name,
|
|
345
|
+
"domain": temp_instance.domain,
|
|
346
|
+
"empathy_level": temp_instance.empathy_level,
|
|
347
|
+
"category": temp_instance.category,
|
|
348
|
+
"required_context": temp_instance.get_required_context(),
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
class PluginError(Exception):
|
|
353
|
+
"""Base exception for plugin-related errors"""
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
class PluginLoadError(PluginError):
|
|
357
|
+
"""Raised when plugin fails to load"""
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
class PluginValidationError(PluginError):
|
|
361
|
+
"""Raised when plugin fails validation"""
|