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,361 @@
|
|
|
1
|
+
"""Contextual Pattern Injection
|
|
2
|
+
|
|
3
|
+
Filters and injects only relevant patterns based on current context.
|
|
4
|
+
Instead of loading all patterns, this module selects patterns that
|
|
5
|
+
are most likely to be useful for the current task.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
from attune_llm.contextual_patterns import ContextualPatternInjector
|
|
9
|
+
|
|
10
|
+
injector = ContextualPatternInjector("./patterns")
|
|
11
|
+
|
|
12
|
+
# Get relevant patterns for a Python file with an async error
|
|
13
|
+
relevant = injector.get_relevant_patterns(
|
|
14
|
+
file_path="src/api.py",
|
|
15
|
+
error_type="async_timing",
|
|
16
|
+
max_patterns=5
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
Author: Empathy Framework Team
|
|
20
|
+
Version: 2.1.3
|
|
21
|
+
License: Fair Source 0.9
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
import json
|
|
25
|
+
import logging
|
|
26
|
+
import subprocess
|
|
27
|
+
from datetime import datetime
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
from typing import Any
|
|
30
|
+
|
|
31
|
+
logger = logging.getLogger(__name__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ContextualPatternInjector:
|
|
35
|
+
"""Injects only relevant patterns based on context.
|
|
36
|
+
|
|
37
|
+
Reduces cognitive load by filtering patterns to those
|
|
38
|
+
most likely to help with the current task.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def __init__(self, patterns_dir: str = "./patterns"):
|
|
42
|
+
self.patterns_dir = Path(patterns_dir)
|
|
43
|
+
self._debugging_dirs = ["debugging", "debugging_demo", "repo_test/debugging"]
|
|
44
|
+
self._security_dirs = ["security", "security_demo", "repo_test/security"]
|
|
45
|
+
self._cache: dict[str, list[dict]] = {}
|
|
46
|
+
|
|
47
|
+
def get_relevant_patterns(
|
|
48
|
+
self,
|
|
49
|
+
file_path: str | None = None,
|
|
50
|
+
error_type: str | None = None,
|
|
51
|
+
error_message: str | None = None,
|
|
52
|
+
max_patterns: int = 5,
|
|
53
|
+
include_security: bool = True,
|
|
54
|
+
) -> str:
|
|
55
|
+
"""Get relevant patterns formatted as markdown.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
file_path: Current file being worked on
|
|
59
|
+
error_type: Type of error (null_reference, async_timing, etc.)
|
|
60
|
+
error_message: Error message text
|
|
61
|
+
max_patterns: Maximum patterns to include
|
|
62
|
+
include_security: Whether to include security decisions
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
Markdown string with relevant patterns
|
|
66
|
+
|
|
67
|
+
"""
|
|
68
|
+
all_bugs = self._load_all_bugs()
|
|
69
|
+
all_security = self._load_all_security() if include_security else []
|
|
70
|
+
|
|
71
|
+
# Score and filter patterns
|
|
72
|
+
scored_bugs = self._score_bugs(all_bugs, file_path, error_type, error_message)
|
|
73
|
+
top_bugs = scored_bugs[:max_patterns]
|
|
74
|
+
|
|
75
|
+
# Filter security decisions by file extension
|
|
76
|
+
relevant_security = self._filter_security(all_security, file_path)
|
|
77
|
+
|
|
78
|
+
return self._format_markdown(top_bugs, relevant_security[:3])
|
|
79
|
+
|
|
80
|
+
def get_patterns_for_review(
|
|
81
|
+
self,
|
|
82
|
+
files: list[str],
|
|
83
|
+
max_per_file: int = 3,
|
|
84
|
+
) -> dict[str, list[dict]]:
|
|
85
|
+
"""Get relevant patterns for code review of multiple files.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
files: List of file paths being reviewed
|
|
89
|
+
max_per_file: Maximum patterns per file
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
Dict mapping file paths to relevant patterns
|
|
93
|
+
|
|
94
|
+
"""
|
|
95
|
+
all_bugs = self._load_all_bugs()
|
|
96
|
+
result = {}
|
|
97
|
+
|
|
98
|
+
for file_path in files:
|
|
99
|
+
file_ext = Path(file_path).suffix
|
|
100
|
+
relevant = [
|
|
101
|
+
bug
|
|
102
|
+
for bug in all_bugs
|
|
103
|
+
if self._file_type_matches(bug.get("file_path", ""), file_ext)
|
|
104
|
+
and bug.get("status") == "resolved"
|
|
105
|
+
]
|
|
106
|
+
result[file_path] = relevant[:max_per_file]
|
|
107
|
+
|
|
108
|
+
return result
|
|
109
|
+
|
|
110
|
+
def get_patterns_from_git_changes(self, max_patterns: int = 5) -> str:
|
|
111
|
+
"""Get relevant patterns based on recently changed files.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Markdown with patterns relevant to git changes
|
|
115
|
+
|
|
116
|
+
"""
|
|
117
|
+
changed_files = self._get_git_changed_files()
|
|
118
|
+
if not changed_files:
|
|
119
|
+
return "No recent git changes detected.\n"
|
|
120
|
+
|
|
121
|
+
# Collect file types
|
|
122
|
+
extensions = set()
|
|
123
|
+
for f in changed_files:
|
|
124
|
+
ext = Path(f).suffix
|
|
125
|
+
if ext:
|
|
126
|
+
extensions.add(ext)
|
|
127
|
+
|
|
128
|
+
all_bugs = self._load_all_bugs()
|
|
129
|
+
|
|
130
|
+
# Filter by file types in changes
|
|
131
|
+
relevant = []
|
|
132
|
+
for bug in all_bugs:
|
|
133
|
+
bug_ext = Path(bug.get("file_path", "")).suffix
|
|
134
|
+
if bug_ext in extensions and bug.get("status") == "resolved":
|
|
135
|
+
relevant.append(bug)
|
|
136
|
+
|
|
137
|
+
return self._format_markdown(relevant[:max_patterns], [])
|
|
138
|
+
|
|
139
|
+
def _load_all_bugs(self) -> list[dict[str, Any]]:
|
|
140
|
+
"""Load all bug patterns from storage."""
|
|
141
|
+
if "bugs" in self._cache:
|
|
142
|
+
return self._cache["bugs"]
|
|
143
|
+
|
|
144
|
+
bugs = []
|
|
145
|
+
for debug_dir in self._debugging_dirs:
|
|
146
|
+
dir_path = self.patterns_dir / debug_dir
|
|
147
|
+
if not dir_path.exists():
|
|
148
|
+
continue
|
|
149
|
+
|
|
150
|
+
for json_file in dir_path.glob("bug_*.json"):
|
|
151
|
+
try:
|
|
152
|
+
with open(json_file, encoding="utf-8") as f:
|
|
153
|
+
bug = json.load(f)
|
|
154
|
+
bug["_source"] = str(json_file)
|
|
155
|
+
bugs.append(bug)
|
|
156
|
+
except (json.JSONDecodeError, OSError):
|
|
157
|
+
continue
|
|
158
|
+
|
|
159
|
+
self._cache["bugs"] = bugs
|
|
160
|
+
return bugs
|
|
161
|
+
|
|
162
|
+
def _load_all_security(self) -> list[dict[str, Any]]:
|
|
163
|
+
"""Load all security decisions from storage."""
|
|
164
|
+
if "security" in self._cache:
|
|
165
|
+
return self._cache["security"]
|
|
166
|
+
|
|
167
|
+
decisions = []
|
|
168
|
+
for sec_dir in self._security_dirs:
|
|
169
|
+
decisions_file = self.patterns_dir / sec_dir / "team_decisions.json"
|
|
170
|
+
if not decisions_file.exists():
|
|
171
|
+
continue
|
|
172
|
+
|
|
173
|
+
try:
|
|
174
|
+
with open(decisions_file, encoding="utf-8") as f:
|
|
175
|
+
data = json.load(f)
|
|
176
|
+
decisions.extend(data.get("decisions", []))
|
|
177
|
+
except (json.JSONDecodeError, OSError):
|
|
178
|
+
continue
|
|
179
|
+
|
|
180
|
+
self._cache["security"] = decisions
|
|
181
|
+
return decisions
|
|
182
|
+
|
|
183
|
+
def _score_bugs(
|
|
184
|
+
self,
|
|
185
|
+
bugs: list[dict],
|
|
186
|
+
file_path: str | None,
|
|
187
|
+
error_type: str | None,
|
|
188
|
+
error_message: str | None,
|
|
189
|
+
) -> list[dict]:
|
|
190
|
+
"""Score bugs by relevance to current context."""
|
|
191
|
+
scored = []
|
|
192
|
+
|
|
193
|
+
for bug in bugs:
|
|
194
|
+
score = 0.0
|
|
195
|
+
|
|
196
|
+
# Resolved bugs are more valuable
|
|
197
|
+
if bug.get("status") == "resolved":
|
|
198
|
+
score += 0.3
|
|
199
|
+
|
|
200
|
+
# Error type match is strongest signal
|
|
201
|
+
if error_type and bug.get("error_type") == error_type:
|
|
202
|
+
score += 0.5
|
|
203
|
+
|
|
204
|
+
# File extension match
|
|
205
|
+
if file_path:
|
|
206
|
+
current_ext = Path(file_path).suffix
|
|
207
|
+
bug_ext = Path(bug.get("file_path", "")).suffix
|
|
208
|
+
if current_ext == bug_ext:
|
|
209
|
+
score += 0.2
|
|
210
|
+
|
|
211
|
+
# Error message similarity (simple keyword match)
|
|
212
|
+
if error_message:
|
|
213
|
+
bug_msg = bug.get("error_message", "").lower()
|
|
214
|
+
error_lower = error_message.lower()
|
|
215
|
+
common_words = set(bug_msg.split()) & set(error_lower.split())
|
|
216
|
+
if common_words:
|
|
217
|
+
score += min(len(common_words) * 0.05, 0.2)
|
|
218
|
+
|
|
219
|
+
# Recent bugs slightly preferred
|
|
220
|
+
try:
|
|
221
|
+
bug_date = datetime.fromisoformat(bug.get("date", "").replace("Z", "+00:00"))
|
|
222
|
+
days_old = (datetime.now(bug_date.tzinfo) - bug_date).days
|
|
223
|
+
if days_old < 30:
|
|
224
|
+
score += 0.1
|
|
225
|
+
except (ValueError, TypeError):
|
|
226
|
+
pass
|
|
227
|
+
|
|
228
|
+
bug["_relevance_score"] = score
|
|
229
|
+
scored.append(bug)
|
|
230
|
+
|
|
231
|
+
# Sort by relevance
|
|
232
|
+
return sorted(scored, key=lambda b: b.get("_relevance_score", 0), reverse=True)
|
|
233
|
+
|
|
234
|
+
def _filter_security(
|
|
235
|
+
self,
|
|
236
|
+
decisions: list[dict],
|
|
237
|
+
file_path: str | None,
|
|
238
|
+
) -> list[dict]:
|
|
239
|
+
"""Filter security decisions relevant to current file."""
|
|
240
|
+
if not file_path:
|
|
241
|
+
return decisions[:3] # Return top 3 general
|
|
242
|
+
|
|
243
|
+
# Map file extensions to security concern areas
|
|
244
|
+
ext_security_map = {
|
|
245
|
+
".py": ["sql_injection", "command_injection", "path_traversal"],
|
|
246
|
+
".js": ["xss", "insecure_random"],
|
|
247
|
+
".ts": ["xss", "insecure_random"],
|
|
248
|
+
".tsx": ["xss"],
|
|
249
|
+
".sql": ["sql_injection"],
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
current_ext = Path(file_path).suffix
|
|
253
|
+
relevant_types = ext_security_map.get(current_ext, [])
|
|
254
|
+
|
|
255
|
+
filtered = [d for d in decisions if d.get("finding_hash", "") in relevant_types]
|
|
256
|
+
|
|
257
|
+
return filtered or decisions[:2] # Fallback to top 2
|
|
258
|
+
|
|
259
|
+
def _file_type_matches(self, bug_path: str, target_ext: str) -> bool:
|
|
260
|
+
"""Check if bug's file type matches target extension."""
|
|
261
|
+
return Path(bug_path).suffix == target_ext
|
|
262
|
+
|
|
263
|
+
def _get_git_changed_files(self) -> list[str]:
|
|
264
|
+
"""Get list of recently changed files from git."""
|
|
265
|
+
try:
|
|
266
|
+
result = subprocess.run(
|
|
267
|
+
["git", "diff", "--name-only", "HEAD~5", "HEAD"],
|
|
268
|
+
check=False,
|
|
269
|
+
capture_output=True,
|
|
270
|
+
text=True,
|
|
271
|
+
timeout=5,
|
|
272
|
+
)
|
|
273
|
+
if result.returncode == 0:
|
|
274
|
+
return [f.strip() for f in result.stdout.strip().split("\n") if f.strip()]
|
|
275
|
+
except subprocess.SubprocessError as e:
|
|
276
|
+
logger.debug(f"Git command failed: {e}")
|
|
277
|
+
except Exception: # noqa: BLE001
|
|
278
|
+
# INTENTIONAL: Git availability detection - don't crash on git errors
|
|
279
|
+
logger.debug("Could not get git changed files (expected in non-git directories)")
|
|
280
|
+
return []
|
|
281
|
+
|
|
282
|
+
def _format_markdown(
|
|
283
|
+
self,
|
|
284
|
+
bugs: list[dict],
|
|
285
|
+
security: list[dict],
|
|
286
|
+
) -> str:
|
|
287
|
+
"""Format patterns as concise markdown."""
|
|
288
|
+
lines = [
|
|
289
|
+
"# Relevant Patterns",
|
|
290
|
+
"",
|
|
291
|
+
]
|
|
292
|
+
|
|
293
|
+
if bugs:
|
|
294
|
+
lines.append("## Bug Patterns")
|
|
295
|
+
lines.append("")
|
|
296
|
+
for bug in bugs:
|
|
297
|
+
status = bug.get("status", "unknown")
|
|
298
|
+
icon = "✓" if status == "resolved" else "?"
|
|
299
|
+
lines.append(f"- **{bug.get('bug_id', 'unknown')}** [{icon}]")
|
|
300
|
+
lines.append(f" - Type: {bug.get('error_type', 'unknown')}")
|
|
301
|
+
if bug.get("root_cause"):
|
|
302
|
+
lines.append(f" - Cause: {bug.get('root_cause')}")
|
|
303
|
+
if bug.get("fix_applied"):
|
|
304
|
+
lines.append(f" - Fix: {bug.get('fix_applied')}")
|
|
305
|
+
if bug.get("fix_code"):
|
|
306
|
+
lines.append(f" - Code: `{bug.get('fix_code')}`")
|
|
307
|
+
lines.append("")
|
|
308
|
+
else:
|
|
309
|
+
lines.append("*No matching bug patterns.*")
|
|
310
|
+
lines.append("")
|
|
311
|
+
|
|
312
|
+
if security:
|
|
313
|
+
lines.append("## Security Decisions")
|
|
314
|
+
lines.append("")
|
|
315
|
+
for decision in security:
|
|
316
|
+
lines.append(
|
|
317
|
+
f"- **{decision.get('finding_hash', '?')}**: {decision.get('decision', '?')}",
|
|
318
|
+
)
|
|
319
|
+
lines.append(f" - Reason: {decision.get('reason', 'N/A')}")
|
|
320
|
+
lines.append("")
|
|
321
|
+
|
|
322
|
+
return "\n".join(lines)
|
|
323
|
+
|
|
324
|
+
def clear_cache(self) -> None:
|
|
325
|
+
"""Clear the pattern cache."""
|
|
326
|
+
self._cache.clear()
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def main():
|
|
330
|
+
"""CLI entry point for contextual patterns."""
|
|
331
|
+
import argparse
|
|
332
|
+
|
|
333
|
+
parser = argparse.ArgumentParser(description="Get contextually relevant patterns")
|
|
334
|
+
parser.add_argument("--file", help="Current file path")
|
|
335
|
+
parser.add_argument("--error-type", help="Error type (null_reference, async_timing, etc.)")
|
|
336
|
+
parser.add_argument("--error-message", help="Error message text")
|
|
337
|
+
parser.add_argument("--max", type=int, default=5, help="Max patterns to show")
|
|
338
|
+
parser.add_argument("--git", action="store_true", help="Use git changes for context")
|
|
339
|
+
parser.add_argument("--patterns-dir", default="./patterns", help="Patterns directory")
|
|
340
|
+
|
|
341
|
+
args = parser.parse_args()
|
|
342
|
+
|
|
343
|
+
logging.basicConfig(level=logging.INFO)
|
|
344
|
+
|
|
345
|
+
injector = ContextualPatternInjector(args.patterns_dir)
|
|
346
|
+
|
|
347
|
+
if args.git:
|
|
348
|
+
print(injector.get_patterns_from_git_changes(args.max))
|
|
349
|
+
else:
|
|
350
|
+
print(
|
|
351
|
+
injector.get_relevant_patterns(
|
|
352
|
+
file_path=args.file,
|
|
353
|
+
error_type=args.error_type,
|
|
354
|
+
error_message=args.error_message,
|
|
355
|
+
max_patterns=args.max,
|
|
356
|
+
),
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
if __name__ == "__main__":
|
|
361
|
+
main()
|