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,487 @@
|
|
|
1
|
+
"""CLAUDE.md Auto-Sync Command for Empathy Framework
|
|
2
|
+
|
|
3
|
+
Syncs learned patterns from Empathy's pattern storage to Claude Code's
|
|
4
|
+
.claude/rules/empathy/ directory for native integration.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
empathy sync-claude # One-time sync
|
|
8
|
+
empathy sync-claude --watch # Watch for changes
|
|
9
|
+
empathy sync-claude --dry-run # Show what would be synced
|
|
10
|
+
|
|
11
|
+
Output structure:
|
|
12
|
+
.claude/rules/empathy/
|
|
13
|
+
├── bug-patterns.md # From patterns/debugging/
|
|
14
|
+
├── security-decisions.md # From patterns/security/
|
|
15
|
+
├── tech-debt-hotspots.md # From patterns/tech_debt/
|
|
16
|
+
└── coding-patterns.md # General patterns
|
|
17
|
+
|
|
18
|
+
Copyright 2025 Smart AI Memory, LLC
|
|
19
|
+
Licensed under Fair Source 0.9
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import argparse
|
|
23
|
+
import json
|
|
24
|
+
import sys
|
|
25
|
+
import time
|
|
26
|
+
from datetime import datetime
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
from typing import Any
|
|
29
|
+
|
|
30
|
+
# Output directory structure
|
|
31
|
+
CLAUDE_RULES_DIR = ".claude/rules/empathy"
|
|
32
|
+
|
|
33
|
+
# Pattern source directories
|
|
34
|
+
PATTERN_SOURCES = {
|
|
35
|
+
"debugging": "bug-patterns.md",
|
|
36
|
+
"security": "security-decisions.md",
|
|
37
|
+
"tech_debt": "tech-debt-hotspots.md",
|
|
38
|
+
"inspection": "coding-patterns.md",
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def load_patterns_from_directory(pattern_dir: Path, pattern_type: str) -> list[dict]:
|
|
43
|
+
"""Load all pattern JSON files from a directory.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
pattern_dir: Path to pattern directory
|
|
47
|
+
pattern_type: Type of patterns (debugging, security, etc.)
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
List of pattern dictionaries
|
|
51
|
+
|
|
52
|
+
"""
|
|
53
|
+
patterns: list[dict[str, Any]] = []
|
|
54
|
+
|
|
55
|
+
if not pattern_dir.exists():
|
|
56
|
+
return patterns
|
|
57
|
+
|
|
58
|
+
for json_file in pattern_dir.glob("*.json"):
|
|
59
|
+
try:
|
|
60
|
+
with open(json_file) as f:
|
|
61
|
+
pattern = json.load(f)
|
|
62
|
+
pattern["_source_file"] = str(json_file)
|
|
63
|
+
pattern["_pattern_type"] = pattern_type
|
|
64
|
+
patterns.append(pattern)
|
|
65
|
+
except (json.JSONDecodeError, OSError) as e:
|
|
66
|
+
print(f"Warning: Could not load {json_file}: {e}", file=sys.stderr)
|
|
67
|
+
|
|
68
|
+
return patterns
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def format_bug_patterns_markdown(patterns: list[dict]) -> str:
|
|
72
|
+
"""Format debugging patterns as Claude-compatible markdown.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
patterns: List of bug pattern dictionaries
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Formatted markdown string
|
|
79
|
+
|
|
80
|
+
"""
|
|
81
|
+
lines = [
|
|
82
|
+
"---",
|
|
83
|
+
"paths: **/*.py, **/*.js, **/*.ts",
|
|
84
|
+
"---",
|
|
85
|
+
"",
|
|
86
|
+
"# Bug Patterns (Auto-generated by Empathy)",
|
|
87
|
+
f"Last sync: {datetime.now().strftime('%Y-%m-%d %H:%M')}",
|
|
88
|
+
"",
|
|
89
|
+
"These patterns help identify and fix common bugs based on your team's history.",
|
|
90
|
+
"",
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
# Group by error type
|
|
94
|
+
by_type: dict[str, list[dict]] = {}
|
|
95
|
+
for p in patterns:
|
|
96
|
+
error_type = p.get("error_type", "unknown")
|
|
97
|
+
if error_type not in by_type:
|
|
98
|
+
by_type[error_type] = []
|
|
99
|
+
by_type[error_type].append(p)
|
|
100
|
+
|
|
101
|
+
for error_type, type_patterns in by_type.items():
|
|
102
|
+
type_title = error_type.replace("_", " ").title()
|
|
103
|
+
lines.append(f"## {type_title} Bugs")
|
|
104
|
+
lines.append("")
|
|
105
|
+
|
|
106
|
+
for p in type_patterns[:5]: # Max 5 per type
|
|
107
|
+
error_message = p.get("error_message", "Unknown error")
|
|
108
|
+
root_cause = p.get("root_cause", "")
|
|
109
|
+
fix_applied = p.get("fix_applied", "")
|
|
110
|
+
pattern_id = p.get("pattern_id", "")
|
|
111
|
+
|
|
112
|
+
lines.append(f"### When you see: `{error_message[:100]}`")
|
|
113
|
+
if root_cause:
|
|
114
|
+
lines.append(f"**Root cause:** {root_cause}")
|
|
115
|
+
if fix_applied:
|
|
116
|
+
lines.append(f"**Fix:** {fix_applied}")
|
|
117
|
+
if pattern_id:
|
|
118
|
+
lines.append(f"**Pattern ID:** {pattern_id}")
|
|
119
|
+
lines.append("")
|
|
120
|
+
|
|
121
|
+
return "\n".join(lines)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def format_security_decisions_markdown(patterns: list[dict]) -> str:
|
|
125
|
+
"""Format security decisions as Claude-compatible markdown."""
|
|
126
|
+
lines = [
|
|
127
|
+
"---",
|
|
128
|
+
"paths: **/*.py, **/*.js, **/*.ts",
|
|
129
|
+
"---",
|
|
130
|
+
"",
|
|
131
|
+
"# Security Decisions (Auto-generated by Empathy)",
|
|
132
|
+
f"Last sync: {datetime.now().strftime('%Y-%m-%d %H:%M')}",
|
|
133
|
+
"",
|
|
134
|
+
"Team security decisions and accepted risks. Reference these before flagging issues.",
|
|
135
|
+
"",
|
|
136
|
+
]
|
|
137
|
+
|
|
138
|
+
# Group by decision type
|
|
139
|
+
by_status: dict[str, list[dict]] = {"ACCEPTED": [], "FALSE_POSITIVE": [], "DEFERRED": []}
|
|
140
|
+
|
|
141
|
+
for p in patterns:
|
|
142
|
+
status = p.get("status", "ACCEPTED")
|
|
143
|
+
if status in by_status:
|
|
144
|
+
by_status[status].append(p)
|
|
145
|
+
|
|
146
|
+
if by_status["FALSE_POSITIVE"]:
|
|
147
|
+
lines.append("## False Positives (Do Not Flag)")
|
|
148
|
+
lines.append("")
|
|
149
|
+
for p in by_status["FALSE_POSITIVE"][:10]:
|
|
150
|
+
issue_type = p.get("issue_type", "unknown")
|
|
151
|
+
rationale = p.get("rationale", "")
|
|
152
|
+
decided_by = p.get("decided_by", "")
|
|
153
|
+
lines.append(f"- **{issue_type}**: {rationale}")
|
|
154
|
+
if decided_by:
|
|
155
|
+
lines.append(f" - Decided by: {decided_by}")
|
|
156
|
+
lines.append("")
|
|
157
|
+
|
|
158
|
+
if by_status["ACCEPTED"]:
|
|
159
|
+
lines.append("## Accepted Risks")
|
|
160
|
+
lines.append("")
|
|
161
|
+
for p in by_status["ACCEPTED"][:10]:
|
|
162
|
+
issue_type = p.get("issue_type", "unknown")
|
|
163
|
+
rationale = p.get("rationale", "")
|
|
164
|
+
lines.append(f"- **{issue_type}**: {rationale}")
|
|
165
|
+
lines.append("")
|
|
166
|
+
|
|
167
|
+
if by_status["DEFERRED"]:
|
|
168
|
+
lines.append("## Deferred (To Address Later)")
|
|
169
|
+
lines.append("")
|
|
170
|
+
for p in by_status["DEFERRED"][:10]:
|
|
171
|
+
issue_type = p.get("issue_type", "unknown")
|
|
172
|
+
rationale = p.get("rationale", "")
|
|
173
|
+
lines.append(f"- **{issue_type}**: {rationale}")
|
|
174
|
+
lines.append("")
|
|
175
|
+
|
|
176
|
+
return "\n".join(lines)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def format_tech_debt_markdown(patterns: list[dict]) -> str:
|
|
180
|
+
"""Format tech debt hotspots as Claude-compatible markdown."""
|
|
181
|
+
lines = [
|
|
182
|
+
"---",
|
|
183
|
+
"paths: **/*",
|
|
184
|
+
"---",
|
|
185
|
+
"",
|
|
186
|
+
"# Tech Debt Hotspots (Auto-generated by Empathy)",
|
|
187
|
+
f"Last sync: {datetime.now().strftime('%Y-%m-%d %H:%M')}",
|
|
188
|
+
"",
|
|
189
|
+
"Areas of the codebase with accumulated technical debt.",
|
|
190
|
+
"",
|
|
191
|
+
]
|
|
192
|
+
|
|
193
|
+
# Group by severity
|
|
194
|
+
by_severity: dict[str, list[dict]] = {"critical": [], "high": [], "medium": [], "low": []}
|
|
195
|
+
|
|
196
|
+
for p in patterns:
|
|
197
|
+
# Get debt items from snapshot
|
|
198
|
+
items = p.get("debt_items", [])
|
|
199
|
+
for item in items:
|
|
200
|
+
severity = item.get("severity", "medium")
|
|
201
|
+
if severity in by_severity:
|
|
202
|
+
by_severity[severity].append(item)
|
|
203
|
+
|
|
204
|
+
for severity in ["critical", "high", "medium"]:
|
|
205
|
+
if by_severity[severity]:
|
|
206
|
+
lines.append(f"## {severity.upper()} Priority")
|
|
207
|
+
lines.append("")
|
|
208
|
+
for item in by_severity[severity][:10]:
|
|
209
|
+
file_path = item.get("file", "")
|
|
210
|
+
line = item.get("line", "")
|
|
211
|
+
item_type = item.get("type", "")
|
|
212
|
+
content = item.get("content", "")[:100]
|
|
213
|
+
lines.append(f"- **{file_path}:{line}** ({item_type})")
|
|
214
|
+
if content:
|
|
215
|
+
lines.append(f" `{content}`")
|
|
216
|
+
lines.append("")
|
|
217
|
+
|
|
218
|
+
return "\n".join(lines)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def format_coding_patterns_markdown(patterns: list[dict]) -> str:
|
|
222
|
+
"""Format general coding patterns as Claude-compatible markdown."""
|
|
223
|
+
lines = [
|
|
224
|
+
"---",
|
|
225
|
+
"paths: **/*",
|
|
226
|
+
"---",
|
|
227
|
+
"",
|
|
228
|
+
"# Coding Patterns (Auto-generated by Empathy)",
|
|
229
|
+
f"Last sync: {datetime.now().strftime('%Y-%m-%d %H:%M')}",
|
|
230
|
+
"",
|
|
231
|
+
"Coding patterns and quality findings from automated inspection.",
|
|
232
|
+
"",
|
|
233
|
+
]
|
|
234
|
+
|
|
235
|
+
# Group by category
|
|
236
|
+
by_category: dict[str, list[dict]] = {}
|
|
237
|
+
|
|
238
|
+
for p in patterns:
|
|
239
|
+
category = p.get("category", "general")
|
|
240
|
+
if category not in by_category:
|
|
241
|
+
by_category[category] = []
|
|
242
|
+
by_category[category].append(p)
|
|
243
|
+
|
|
244
|
+
for category, cat_patterns in by_category.items():
|
|
245
|
+
category_title = category.replace("_", " ").title()
|
|
246
|
+
lines.append(f"## {category_title}")
|
|
247
|
+
lines.append("")
|
|
248
|
+
|
|
249
|
+
for p in cat_patterns[:10]:
|
|
250
|
+
description = p.get("description", "")
|
|
251
|
+
recommendation = p.get("recommendation", "")
|
|
252
|
+
|
|
253
|
+
if description:
|
|
254
|
+
lines.append(f"- {description}")
|
|
255
|
+
if recommendation:
|
|
256
|
+
lines.append(f" **Recommendation:** {recommendation}")
|
|
257
|
+
|
|
258
|
+
lines.append("")
|
|
259
|
+
|
|
260
|
+
return "\n".join(lines)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def sync_patterns(
|
|
264
|
+
project_root: Path,
|
|
265
|
+
dry_run: bool = False,
|
|
266
|
+
verbose: bool = False,
|
|
267
|
+
) -> dict[str, Any]:
|
|
268
|
+
"""Sync patterns to .claude/rules/empathy/ directory.
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
project_root: Project root directory
|
|
272
|
+
dry_run: If True, don't write files
|
|
273
|
+
verbose: If True, print detailed output
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
Summary of sync operation
|
|
277
|
+
|
|
278
|
+
"""
|
|
279
|
+
patterns_dir = project_root / "patterns"
|
|
280
|
+
output_dir = project_root / CLAUDE_RULES_DIR
|
|
281
|
+
|
|
282
|
+
results: dict[str, Any] = {
|
|
283
|
+
"synced": [],
|
|
284
|
+
"skipped": [],
|
|
285
|
+
"errors": [],
|
|
286
|
+
"total_patterns": 0,
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if not patterns_dir.exists():
|
|
290
|
+
if verbose:
|
|
291
|
+
print(f"No patterns directory found at {patterns_dir}")
|
|
292
|
+
return results
|
|
293
|
+
|
|
294
|
+
# Create output directory
|
|
295
|
+
if not dry_run:
|
|
296
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
297
|
+
|
|
298
|
+
# Process each pattern type
|
|
299
|
+
formatters = {
|
|
300
|
+
"debugging": format_bug_patterns_markdown,
|
|
301
|
+
"security": format_security_decisions_markdown,
|
|
302
|
+
"tech_debt": format_tech_debt_markdown,
|
|
303
|
+
"inspection": format_coding_patterns_markdown,
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
for pattern_type, output_file in PATTERN_SOURCES.items():
|
|
307
|
+
source_dir = patterns_dir / pattern_type
|
|
308
|
+
patterns = load_patterns_from_directory(source_dir, pattern_type)
|
|
309
|
+
|
|
310
|
+
if not patterns:
|
|
311
|
+
if verbose:
|
|
312
|
+
print(f"No {pattern_type} patterns found")
|
|
313
|
+
results["skipped"].append(pattern_type)
|
|
314
|
+
continue
|
|
315
|
+
|
|
316
|
+
results["total_patterns"] += len(patterns)
|
|
317
|
+
|
|
318
|
+
# Format patterns
|
|
319
|
+
formatter = formatters.get(pattern_type, format_coding_patterns_markdown)
|
|
320
|
+
markdown_content = formatter(patterns)
|
|
321
|
+
|
|
322
|
+
# Write output
|
|
323
|
+
output_path = output_dir / output_file
|
|
324
|
+
|
|
325
|
+
if dry_run:
|
|
326
|
+
print(f"Would write {len(patterns)} patterns to {output_path}")
|
|
327
|
+
if verbose:
|
|
328
|
+
print(f"--- {output_file} ---")
|
|
329
|
+
print(
|
|
330
|
+
(
|
|
331
|
+
markdown_content[:500] + "..."
|
|
332
|
+
if len(markdown_content) > 500
|
|
333
|
+
else markdown_content
|
|
334
|
+
),
|
|
335
|
+
)
|
|
336
|
+
print()
|
|
337
|
+
else:
|
|
338
|
+
try:
|
|
339
|
+
with open(output_path, "w") as f:
|
|
340
|
+
f.write(markdown_content)
|
|
341
|
+
results["synced"].append(
|
|
342
|
+
{
|
|
343
|
+
"type": pattern_type,
|
|
344
|
+
"file": str(output_path),
|
|
345
|
+
"patterns": len(patterns),
|
|
346
|
+
},
|
|
347
|
+
)
|
|
348
|
+
if verbose:
|
|
349
|
+
print(f"Synced {len(patterns)} {pattern_type} patterns to {output_path}")
|
|
350
|
+
except OSError as e:
|
|
351
|
+
results["errors"].append(f"{pattern_type}: {e}")
|
|
352
|
+
if verbose:
|
|
353
|
+
print(f"Error writing {output_path}: {e}", file=sys.stderr)
|
|
354
|
+
|
|
355
|
+
return results
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def watch_and_sync(project_root: Path, interval: int = 30, verbose: bool = False):
|
|
359
|
+
"""Watch patterns directory and sync on changes.
|
|
360
|
+
|
|
361
|
+
Args:
|
|
362
|
+
project_root: Project root directory
|
|
363
|
+
interval: Check interval in seconds
|
|
364
|
+
verbose: Verbose output
|
|
365
|
+
|
|
366
|
+
"""
|
|
367
|
+
print(f"Watching for pattern changes (every {interval}s)...")
|
|
368
|
+
print("Press Ctrl+C to stop")
|
|
369
|
+
|
|
370
|
+
patterns_dir = project_root / "patterns"
|
|
371
|
+
last_mtime = 0.0
|
|
372
|
+
|
|
373
|
+
try:
|
|
374
|
+
while True:
|
|
375
|
+
# Check for changes
|
|
376
|
+
current_mtime = 0.0
|
|
377
|
+
if patterns_dir.exists():
|
|
378
|
+
for json_file in patterns_dir.rglob("*.json"):
|
|
379
|
+
file_mtime = json_file.stat().st_mtime
|
|
380
|
+
current_mtime = max(current_mtime, file_mtime)
|
|
381
|
+
|
|
382
|
+
if current_mtime > last_mtime:
|
|
383
|
+
if last_mtime > 0:
|
|
384
|
+
print(f"\n[{datetime.now().strftime('%H:%M:%S')}] Changes detected, syncing...")
|
|
385
|
+
results = sync_patterns(project_root, verbose=verbose)
|
|
386
|
+
print(f"Synced {results['total_patterns']} patterns")
|
|
387
|
+
else:
|
|
388
|
+
# Initial sync
|
|
389
|
+
results = sync_patterns(project_root, verbose=verbose)
|
|
390
|
+
print(f"Initial sync: {results['total_patterns']} patterns")
|
|
391
|
+
last_mtime = current_mtime
|
|
392
|
+
|
|
393
|
+
time.sleep(interval)
|
|
394
|
+
|
|
395
|
+
except KeyboardInterrupt:
|
|
396
|
+
print("\nStopped watching")
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def main():
|
|
400
|
+
"""Main CLI entry point."""
|
|
401
|
+
parser = argparse.ArgumentParser(
|
|
402
|
+
prog="empathy sync-claude",
|
|
403
|
+
description="Sync Empathy patterns to .claude/rules/empathy/",
|
|
404
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
405
|
+
epilog="""
|
|
406
|
+
Examples:
|
|
407
|
+
empathy sync-claude # Sync patterns once
|
|
408
|
+
empathy sync-claude --watch # Watch and auto-sync
|
|
409
|
+
empathy sync-claude --dry-run # Preview without writing
|
|
410
|
+
empathy sync-claude --verbose # Show detailed output
|
|
411
|
+
|
|
412
|
+
Output structure:
|
|
413
|
+
.claude/rules/empathy/
|
|
414
|
+
├── bug-patterns.md # From patterns/debugging/
|
|
415
|
+
├── security-decisions.md # From patterns/security/
|
|
416
|
+
├── tech-debt-hotspots.md # From patterns/tech_debt/
|
|
417
|
+
└── coding-patterns.md # From patterns/inspection/
|
|
418
|
+
""",
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
parser.add_argument(
|
|
422
|
+
"--project-root",
|
|
423
|
+
"-p",
|
|
424
|
+
type=Path,
|
|
425
|
+
default=Path.cwd(),
|
|
426
|
+
help="Project root directory (default: current directory)",
|
|
427
|
+
)
|
|
428
|
+
parser.add_argument(
|
|
429
|
+
"--watch",
|
|
430
|
+
"-w",
|
|
431
|
+
action="store_true",
|
|
432
|
+
help="Watch for changes and auto-sync",
|
|
433
|
+
)
|
|
434
|
+
parser.add_argument(
|
|
435
|
+
"--interval",
|
|
436
|
+
"-i",
|
|
437
|
+
type=int,
|
|
438
|
+
default=30,
|
|
439
|
+
help="Watch interval in seconds (default: 30)",
|
|
440
|
+
)
|
|
441
|
+
parser.add_argument(
|
|
442
|
+
"--dry-run",
|
|
443
|
+
"-n",
|
|
444
|
+
action="store_true",
|
|
445
|
+
help="Show what would be synced without writing files",
|
|
446
|
+
)
|
|
447
|
+
parser.add_argument(
|
|
448
|
+
"--verbose",
|
|
449
|
+
"-v",
|
|
450
|
+
action="store_true",
|
|
451
|
+
help="Verbose output",
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
args = parser.parse_args()
|
|
455
|
+
|
|
456
|
+
project_root = args.project_root.resolve()
|
|
457
|
+
|
|
458
|
+
if args.verbose:
|
|
459
|
+
print(f"Project root: {project_root}")
|
|
460
|
+
print(f"Patterns dir: {project_root / 'patterns'}")
|
|
461
|
+
print(f"Output dir: {project_root / CLAUDE_RULES_DIR}")
|
|
462
|
+
print()
|
|
463
|
+
|
|
464
|
+
if args.watch:
|
|
465
|
+
watch_and_sync(project_root, args.interval, args.verbose)
|
|
466
|
+
else:
|
|
467
|
+
results = sync_patterns(project_root, args.dry_run, args.verbose)
|
|
468
|
+
|
|
469
|
+
if args.dry_run:
|
|
470
|
+
print(f"\nDry run complete: {results['total_patterns']} patterns would be synced")
|
|
471
|
+
else:
|
|
472
|
+
if results["synced"]:
|
|
473
|
+
print(f"\n✓ Synced {results['total_patterns']} patterns to {CLAUDE_RULES_DIR}/")
|
|
474
|
+
for item in results["synced"]:
|
|
475
|
+
print(f" - {item['type']}: {item['patterns']} patterns")
|
|
476
|
+
elif results["skipped"]:
|
|
477
|
+
print("No patterns to sync")
|
|
478
|
+
|
|
479
|
+
if results["errors"]:
|
|
480
|
+
print("\nErrors:", file=sys.stderr)
|
|
481
|
+
for error in results["errors"]:
|
|
482
|
+
print(f" - {error}", file=sys.stderr)
|
|
483
|
+
sys.exit(1)
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
if __name__ == "__main__":
|
|
487
|
+
main()
|