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,435 @@
|
|
|
1
|
+
"""Git Pattern Extractor
|
|
2
|
+
|
|
3
|
+
Automatically detects bug fixes from git commits and creates
|
|
4
|
+
draft pattern entries for review.
|
|
5
|
+
|
|
6
|
+
This module integrates with git hooks (post-commit) to capture
|
|
7
|
+
fix patterns as they happen.
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
# As a post-commit hook
|
|
11
|
+
python -m attune_llm.git_pattern_extractor
|
|
12
|
+
|
|
13
|
+
# Manual extraction for recent commits
|
|
14
|
+
python -m attune_llm.git_pattern_extractor --commits 5
|
|
15
|
+
|
|
16
|
+
Author: Empathy Framework Team
|
|
17
|
+
Version: 2.1.3
|
|
18
|
+
License: Fair Source 0.9
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import hashlib
|
|
22
|
+
import json
|
|
23
|
+
import logging
|
|
24
|
+
import re
|
|
25
|
+
import subprocess
|
|
26
|
+
from datetime import datetime
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
from typing import Any
|
|
29
|
+
|
|
30
|
+
logger = logging.getLogger(__name__)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class GitPatternExtractor:
|
|
34
|
+
"""Extracts bug fix patterns from git commits.
|
|
35
|
+
|
|
36
|
+
Analyzes commit messages and diffs to detect common
|
|
37
|
+
fix patterns, then creates draft pattern files.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(self, patterns_dir: str = "./patterns"):
|
|
41
|
+
self.patterns_dir = Path(patterns_dir)
|
|
42
|
+
self.debugging_dir = self.patterns_dir / "debugging"
|
|
43
|
+
|
|
44
|
+
# Commit message patterns that indicate fixes
|
|
45
|
+
self._fix_message_patterns = [
|
|
46
|
+
(r"\bfix(?:es|ed)?\b", 1.0), # "fix", "fixes", "fixed"
|
|
47
|
+
(r"\bbug\b", 0.8), # "bug"
|
|
48
|
+
(r"\bresolve[sd]?\b", 0.9), # "resolve", "resolved"
|
|
49
|
+
(r"\brepair[sed]?\b", 0.8), # "repair"
|
|
50
|
+
(r"\bpatch(?:es|ed)?\b", 0.7), # "patch"
|
|
51
|
+
(r"\bcorrect[sed]?\b", 0.7), # "correct"
|
|
52
|
+
(r"\bhotfix\b", 1.0), # "hotfix"
|
|
53
|
+
(r"#\d+", 0.6), # Issue references
|
|
54
|
+
(r"\bclose[sd]?\s+#\d+", 0.9), # "closes #123"
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
# Code patterns that indicate specific fix types
|
|
58
|
+
self._code_fix_patterns = {
|
|
59
|
+
"null_reference": {
|
|
60
|
+
"patterns": [
|
|
61
|
+
r"\?\.", # Optional chaining added
|
|
62
|
+
r"\?\?\s*\[", # Nullish coalescing with array
|
|
63
|
+
r"\.get\s*\([^,]+,\s*", # Python .get() with default
|
|
64
|
+
r"if\s*\(\s*\w+\s*(!=|!==)\s*(null|undefined)", # Null check
|
|
65
|
+
r"or\s*\[\]", # Python or [] fallback
|
|
66
|
+
],
|
|
67
|
+
"description": "Null/undefined reference fix",
|
|
68
|
+
},
|
|
69
|
+
"async_timing": {
|
|
70
|
+
"patterns": [
|
|
71
|
+
r"\bawait\s+\w+", # Added await
|
|
72
|
+
r"async\s+def\s+", # Made async
|
|
73
|
+
r"\.then\s*\([^)]*\)\s*\.\s*catch", # Added error handling
|
|
74
|
+
],
|
|
75
|
+
"description": "Async/timing fix",
|
|
76
|
+
},
|
|
77
|
+
"error_handling": {
|
|
78
|
+
"patterns": [
|
|
79
|
+
r"\btry\s*[:\{]", # Try block
|
|
80
|
+
r"\bexcept\s+\w+", # Python except
|
|
81
|
+
r"\.catch\s*\(", # Promise catch
|
|
82
|
+
r"\bcatch\s*\(", # Try-catch
|
|
83
|
+
],
|
|
84
|
+
"description": "Error handling improvement",
|
|
85
|
+
},
|
|
86
|
+
"type_mismatch": {
|
|
87
|
+
"patterns": [
|
|
88
|
+
r":\s*(str|int|float|bool)", # Python type hints
|
|
89
|
+
r"isinstance\s*\(", # Type check
|
|
90
|
+
r"typeof\s+\w+\s*===", # JS typeof
|
|
91
|
+
],
|
|
92
|
+
"description": "Type mismatch fix",
|
|
93
|
+
},
|
|
94
|
+
"import_error": {
|
|
95
|
+
"patterns": [
|
|
96
|
+
r"from\s+\w+\s+import", # Python import
|
|
97
|
+
r"import\s+\{[^}]+\}\s+from", # ES6 import
|
|
98
|
+
r"require\s*\(['\"]", # CommonJS require
|
|
99
|
+
],
|
|
100
|
+
"description": "Import/dependency fix",
|
|
101
|
+
},
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
def extract_from_recent_commits(self, num_commits: int = 1) -> list[dict[str, Any]]:
|
|
105
|
+
"""Extract patterns from recent git commits.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
num_commits: Number of recent commits to analyze
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
List of detected pattern dicts
|
|
112
|
+
|
|
113
|
+
"""
|
|
114
|
+
patterns = []
|
|
115
|
+
|
|
116
|
+
for i in range(num_commits):
|
|
117
|
+
commit_info = self._get_commit_info(f"HEAD~{i}")
|
|
118
|
+
if not commit_info:
|
|
119
|
+
continue
|
|
120
|
+
|
|
121
|
+
# Check if this looks like a fix commit
|
|
122
|
+
fix_score = self._score_fix_commit(commit_info["message"])
|
|
123
|
+
if fix_score < 0.5:
|
|
124
|
+
continue
|
|
125
|
+
|
|
126
|
+
# Analyze the diff for this commit
|
|
127
|
+
diff = self._get_commit_diff(f"HEAD~{i + 1}", f"HEAD~{i}")
|
|
128
|
+
detected = self._analyze_diff(diff, commit_info)
|
|
129
|
+
|
|
130
|
+
for pattern in detected:
|
|
131
|
+
pattern["fix_score"] = fix_score
|
|
132
|
+
pattern["commit_hash"] = commit_info["hash"]
|
|
133
|
+
pattern["commit_message"] = commit_info["message"]
|
|
134
|
+
patterns.append(pattern)
|
|
135
|
+
|
|
136
|
+
return patterns
|
|
137
|
+
|
|
138
|
+
def extract_from_staged(self) -> list[dict[str, Any]]:
|
|
139
|
+
"""Extract patterns from currently staged changes.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
List of detected pattern dicts
|
|
143
|
+
|
|
144
|
+
"""
|
|
145
|
+
diff = self._get_staged_diff()
|
|
146
|
+
if not diff:
|
|
147
|
+
return []
|
|
148
|
+
|
|
149
|
+
commit_info = {
|
|
150
|
+
"hash": "staged",
|
|
151
|
+
"message": "staged changes",
|
|
152
|
+
"author": self._get_git_config("user.name") or "developer",
|
|
153
|
+
"date": datetime.now().isoformat(),
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return self._analyze_diff(diff, commit_info)
|
|
157
|
+
|
|
158
|
+
def save_pattern(self, pattern: dict[str, Any]) -> Path | None:
|
|
159
|
+
"""Save a detected pattern as a draft for review.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
pattern: Pattern dict from extraction
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
Path to saved file, or None if failed
|
|
166
|
+
|
|
167
|
+
"""
|
|
168
|
+
self.debugging_dir.mkdir(parents=True, exist_ok=True)
|
|
169
|
+
|
|
170
|
+
# Generate pattern file
|
|
171
|
+
pattern_data = {
|
|
172
|
+
"bug_id": pattern["pattern_id"],
|
|
173
|
+
"date": datetime.now().isoformat(),
|
|
174
|
+
"file_path": pattern.get("file", "unknown"),
|
|
175
|
+
"error_type": pattern.get("type", "unknown"),
|
|
176
|
+
"error_message": f"From commit: {pattern.get('commit_message', '')[:80]}",
|
|
177
|
+
"root_cause": "", # To be filled by user
|
|
178
|
+
"fix_applied": pattern.get("description", ""),
|
|
179
|
+
"fix_code": pattern.get("code_sample", ""),
|
|
180
|
+
"status": "investigating", # Draft status
|
|
181
|
+
"resolved_by": pattern.get("author", "@developer"),
|
|
182
|
+
"auto_detected": True,
|
|
183
|
+
"commit_hash": pattern.get("commit_hash", ""),
|
|
184
|
+
"fix_score": pattern.get("fix_score", 0.0),
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
output_file = self.debugging_dir / f"{pattern['pattern_id']}.json"
|
|
188
|
+
|
|
189
|
+
try:
|
|
190
|
+
with open(output_file, "w", encoding="utf-8") as f:
|
|
191
|
+
json.dump(pattern_data, f, indent=2, default=str)
|
|
192
|
+
return output_file
|
|
193
|
+
except OSError as e:
|
|
194
|
+
logger.error("Failed to save pattern: %s", e)
|
|
195
|
+
return None
|
|
196
|
+
|
|
197
|
+
def _get_commit_info(self, ref: str) -> dict[str, str] | None:
|
|
198
|
+
"""Get info about a specific commit."""
|
|
199
|
+
try:
|
|
200
|
+
result = subprocess.run(
|
|
201
|
+
["git", "log", "-1", "--format=%H%n%s%n%an%n%aI", ref],
|
|
202
|
+
check=False,
|
|
203
|
+
capture_output=True,
|
|
204
|
+
text=True,
|
|
205
|
+
timeout=5,
|
|
206
|
+
)
|
|
207
|
+
if result.returncode != 0:
|
|
208
|
+
return None
|
|
209
|
+
|
|
210
|
+
lines = result.stdout.strip().split("\n")
|
|
211
|
+
if len(lines) < 4:
|
|
212
|
+
return None
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
"hash": lines[0][:8],
|
|
216
|
+
"message": lines[1],
|
|
217
|
+
"author": lines[2],
|
|
218
|
+
"date": lines[3],
|
|
219
|
+
}
|
|
220
|
+
except Exception: # noqa: BLE001
|
|
221
|
+
# INTENTIONAL: Git commands may fail for various reasons (not a repo, detached HEAD, etc.)
|
|
222
|
+
return None
|
|
223
|
+
|
|
224
|
+
def _get_commit_diff(self, ref1: str, ref2: str) -> str:
|
|
225
|
+
"""Get diff between two commits."""
|
|
226
|
+
try:
|
|
227
|
+
result = subprocess.run(
|
|
228
|
+
["git", "diff", ref1, ref2],
|
|
229
|
+
check=False,
|
|
230
|
+
capture_output=True,
|
|
231
|
+
text=True,
|
|
232
|
+
timeout=10,
|
|
233
|
+
)
|
|
234
|
+
return result.stdout if result.returncode == 0 else ""
|
|
235
|
+
except Exception: # noqa: BLE001
|
|
236
|
+
# INTENTIONAL: Git commands may fail for various reasons (not a repo, no commits, etc.)
|
|
237
|
+
return ""
|
|
238
|
+
|
|
239
|
+
def _get_staged_diff(self) -> str:
|
|
240
|
+
"""Get diff of staged changes."""
|
|
241
|
+
try:
|
|
242
|
+
result = subprocess.run(
|
|
243
|
+
["git", "diff", "--cached"],
|
|
244
|
+
check=False,
|
|
245
|
+
capture_output=True,
|
|
246
|
+
text=True,
|
|
247
|
+
timeout=10,
|
|
248
|
+
)
|
|
249
|
+
return result.stdout if result.returncode == 0 else ""
|
|
250
|
+
except Exception: # noqa: BLE001
|
|
251
|
+
# INTENTIONAL: Git commands may fail for various reasons (not a repo, nothing staged, etc.)
|
|
252
|
+
return ""
|
|
253
|
+
|
|
254
|
+
def _get_git_config(self, key: str) -> str | None:
|
|
255
|
+
"""Get a git config value."""
|
|
256
|
+
try:
|
|
257
|
+
result = subprocess.run(
|
|
258
|
+
["git", "config", key],
|
|
259
|
+
check=False,
|
|
260
|
+
capture_output=True,
|
|
261
|
+
text=True,
|
|
262
|
+
timeout=5,
|
|
263
|
+
)
|
|
264
|
+
return result.stdout.strip() if result.returncode == 0 else None
|
|
265
|
+
except Exception: # noqa: BLE001
|
|
266
|
+
# INTENTIONAL: Git config may fail (not a repo, key not set, etc.)
|
|
267
|
+
return None
|
|
268
|
+
|
|
269
|
+
def _score_fix_commit(self, message: str) -> float:
|
|
270
|
+
"""Score how likely a commit message indicates a fix."""
|
|
271
|
+
message_lower = message.lower()
|
|
272
|
+
max_score = 0.0
|
|
273
|
+
|
|
274
|
+
for pattern, score in self._fix_message_patterns:
|
|
275
|
+
if re.search(pattern, message_lower, re.IGNORECASE):
|
|
276
|
+
max_score = max(max_score, score)
|
|
277
|
+
|
|
278
|
+
return max_score
|
|
279
|
+
|
|
280
|
+
def _analyze_diff(
|
|
281
|
+
self,
|
|
282
|
+
diff: str,
|
|
283
|
+
commit_info: dict[str, str],
|
|
284
|
+
) -> list[dict[str, Any]]:
|
|
285
|
+
"""Analyze a diff for fix patterns."""
|
|
286
|
+
patterns = []
|
|
287
|
+
current_file = ""
|
|
288
|
+
added_lines: list[str] = []
|
|
289
|
+
|
|
290
|
+
for line in diff.split("\n"):
|
|
291
|
+
if line.startswith("diff --git"):
|
|
292
|
+
# Process previous file
|
|
293
|
+
if current_file and added_lines:
|
|
294
|
+
file_patterns = self._detect_fix_patterns(
|
|
295
|
+
current_file,
|
|
296
|
+
added_lines,
|
|
297
|
+
commit_info,
|
|
298
|
+
)
|
|
299
|
+
patterns.extend(file_patterns)
|
|
300
|
+
|
|
301
|
+
# Start new file
|
|
302
|
+
match = re.search(r"b/(.+)$", line)
|
|
303
|
+
current_file = match.group(1) if match else ""
|
|
304
|
+
added_lines = []
|
|
305
|
+
|
|
306
|
+
elif line.startswith("+") and not line.startswith("+++"):
|
|
307
|
+
added_lines.append(line[1:])
|
|
308
|
+
|
|
309
|
+
# Process last file
|
|
310
|
+
if current_file and added_lines:
|
|
311
|
+
file_patterns = self._detect_fix_patterns(current_file, added_lines, commit_info)
|
|
312
|
+
patterns.extend(file_patterns)
|
|
313
|
+
|
|
314
|
+
return patterns
|
|
315
|
+
|
|
316
|
+
def _detect_fix_patterns(
|
|
317
|
+
self,
|
|
318
|
+
file_path: str,
|
|
319
|
+
added_lines: list[str],
|
|
320
|
+
commit_info: dict[str, str],
|
|
321
|
+
) -> list[dict[str, Any]]:
|
|
322
|
+
"""Detect fix patterns in added lines."""
|
|
323
|
+
detected = []
|
|
324
|
+
added_content = "\n".join(added_lines)
|
|
325
|
+
|
|
326
|
+
for fix_type, config in self._code_fix_patterns.items():
|
|
327
|
+
matches = []
|
|
328
|
+
for pattern in config["patterns"]:
|
|
329
|
+
found = re.findall(pattern, added_content)
|
|
330
|
+
if found:
|
|
331
|
+
matches.extend(found)
|
|
332
|
+
|
|
333
|
+
if matches:
|
|
334
|
+
pattern_id = self._generate_pattern_id(file_path, fix_type)
|
|
335
|
+
detected.append(
|
|
336
|
+
{
|
|
337
|
+
"pattern_id": pattern_id,
|
|
338
|
+
"type": fix_type,
|
|
339
|
+
"file": file_path,
|
|
340
|
+
"description": config["description"],
|
|
341
|
+
"code_sample": matches[0] if matches else "",
|
|
342
|
+
"matches_count": len(matches),
|
|
343
|
+
"author": commit_info.get("author", "unknown"),
|
|
344
|
+
"date": commit_info.get("date", datetime.now().isoformat()),
|
|
345
|
+
},
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
return detected
|
|
349
|
+
|
|
350
|
+
def _generate_pattern_id(self, file_path: str, pattern_type: str) -> str:
|
|
351
|
+
"""Generate unique pattern ID."""
|
|
352
|
+
date_str = datetime.now().strftime("%Y%m%d")
|
|
353
|
+
content = f"{file_path}:{pattern_type}:{datetime.now().isoformat()}"
|
|
354
|
+
hash_suffix = hashlib.md5(content.encode(), usedforsecurity=False).hexdigest()[:8]
|
|
355
|
+
return f"bug_{date_str}_{hash_suffix}"
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def main():
|
|
359
|
+
"""CLI entry point for git pattern extraction."""
|
|
360
|
+
import argparse
|
|
361
|
+
|
|
362
|
+
parser = argparse.ArgumentParser(
|
|
363
|
+
description="Extract bug fix patterns from git commits",
|
|
364
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
365
|
+
epilog="""
|
|
366
|
+
Examples:
|
|
367
|
+
# Extract from last commit (post-commit hook)
|
|
368
|
+
python -m attune_llm.git_pattern_extractor
|
|
369
|
+
|
|
370
|
+
# Extract from last 5 commits
|
|
371
|
+
python -m attune_llm.git_pattern_extractor --commits 5
|
|
372
|
+
|
|
373
|
+
# Extract from staged changes
|
|
374
|
+
python -m attune_llm.git_pattern_extractor --staged
|
|
375
|
+
|
|
376
|
+
# Save detected patterns
|
|
377
|
+
python -m attune_llm.git_pattern_extractor --save
|
|
378
|
+
""",
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
parser.add_argument("--commits", type=int, default=1, help="Number of commits to analyze")
|
|
382
|
+
parser.add_argument("--staged", action="store_true", help="Analyze staged changes instead")
|
|
383
|
+
parser.add_argument("--save", action="store_true", help="Save detected patterns to files")
|
|
384
|
+
parser.add_argument("--patterns-dir", default="./patterns", help="Patterns directory")
|
|
385
|
+
parser.add_argument("--quiet", action="store_true", help="Suppress output (for hooks)")
|
|
386
|
+
|
|
387
|
+
args = parser.parse_args()
|
|
388
|
+
|
|
389
|
+
# Configure logging
|
|
390
|
+
log_level = logging.WARNING if args.quiet else logging.INFO
|
|
391
|
+
logging.basicConfig(level=log_level, format="%(levelname)s: %(message)s")
|
|
392
|
+
|
|
393
|
+
extractor = GitPatternExtractor(args.patterns_dir)
|
|
394
|
+
|
|
395
|
+
# Extract patterns
|
|
396
|
+
if args.staged:
|
|
397
|
+
patterns = extractor.extract_from_staged()
|
|
398
|
+
else:
|
|
399
|
+
patterns = extractor.extract_from_recent_commits(args.commits)
|
|
400
|
+
|
|
401
|
+
if not patterns:
|
|
402
|
+
if not args.quiet:
|
|
403
|
+
print("No fix patterns detected.")
|
|
404
|
+
return
|
|
405
|
+
|
|
406
|
+
# Display or save patterns
|
|
407
|
+
if not args.quiet:
|
|
408
|
+
print(f"\n{'=' * 50}")
|
|
409
|
+
print(f"Detected {len(patterns)} fix pattern(s)")
|
|
410
|
+
print(f"{'=' * 50}\n")
|
|
411
|
+
|
|
412
|
+
for pattern in patterns:
|
|
413
|
+
if not args.quiet:
|
|
414
|
+
print(f" Pattern: {pattern['pattern_id']}")
|
|
415
|
+
print(f" Type: {pattern['type']}")
|
|
416
|
+
print(f" File: {pattern['file']}")
|
|
417
|
+
print(f" Description: {pattern['description']}")
|
|
418
|
+
if "commit_message" in pattern:
|
|
419
|
+
print(f" Commit: {pattern['commit_message'][:60]}...")
|
|
420
|
+
print(f" Confidence: {pattern.get('fix_score', 0):.0%}")
|
|
421
|
+
print()
|
|
422
|
+
|
|
423
|
+
if args.save:
|
|
424
|
+
saved_path = extractor.save_pattern(pattern)
|
|
425
|
+
if saved_path and not args.quiet:
|
|
426
|
+
print(f" ✓ Saved to: {saved_path}")
|
|
427
|
+
print()
|
|
428
|
+
|
|
429
|
+
if args.save and not args.quiet:
|
|
430
|
+
print("\nPatterns saved with 'investigating' status.")
|
|
431
|
+
print("Use 'empathy patterns resolve <id>' to complete them.")
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
if __name__ == "__main__":
|
|
435
|
+
main()
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Hook System for Empathy Framework
|
|
2
|
+
|
|
3
|
+
Event-driven automation system for Empathy Framework.
|
|
4
|
+
Supports PreToolUse, PostToolUse, SessionStart, SessionEnd, PreCompact, and Stop events.
|
|
5
|
+
|
|
6
|
+
Architectural patterns inspired by everything-claude-code by Affaan Mustafa.
|
|
7
|
+
See: https://github.com/affaan-m/everything-claude-code (MIT License)
|
|
8
|
+
See: ACKNOWLEDGMENTS.md for full attribution.
|
|
9
|
+
|
|
10
|
+
Copyright 2025 Smart-AI-Memory
|
|
11
|
+
Licensed under Fair Source License 0.9
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from attune_llm.hooks.config import HookConfig, HookDefinition, HookEvent
|
|
15
|
+
from attune_llm.hooks.executor import HookExecutor
|
|
16
|
+
from attune_llm.hooks.registry import HookRegistry
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"HookConfig",
|
|
20
|
+
"HookDefinition",
|
|
21
|
+
"HookEvent",
|
|
22
|
+
"HookExecutor",
|
|
23
|
+
"HookRegistry",
|
|
24
|
+
]
|