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.
Files changed (457) hide show
  1. attune/__init__.py +358 -0
  2. attune/adaptive/__init__.py +13 -0
  3. attune/adaptive/task_complexity.py +127 -0
  4. attune/agent_monitoring.py +414 -0
  5. attune/cache/__init__.py +117 -0
  6. attune/cache/base.py +166 -0
  7. attune/cache/dependency_manager.py +256 -0
  8. attune/cache/hash_only.py +251 -0
  9. attune/cache/hybrid.py +457 -0
  10. attune/cache/storage.py +285 -0
  11. attune/cache_monitor.py +356 -0
  12. attune/cache_stats.py +298 -0
  13. attune/cli/__init__.py +152 -0
  14. attune/cli/__main__.py +12 -0
  15. attune/cli/commands/__init__.py +1 -0
  16. attune/cli/commands/batch.py +264 -0
  17. attune/cli/commands/cache.py +248 -0
  18. attune/cli/commands/help.py +331 -0
  19. attune/cli/commands/info.py +140 -0
  20. attune/cli/commands/inspect.py +436 -0
  21. attune/cli/commands/inspection.py +57 -0
  22. attune/cli/commands/memory.py +48 -0
  23. attune/cli/commands/metrics.py +92 -0
  24. attune/cli/commands/orchestrate.py +184 -0
  25. attune/cli/commands/patterns.py +207 -0
  26. attune/cli/commands/profiling.py +202 -0
  27. attune/cli/commands/provider.py +98 -0
  28. attune/cli/commands/routing.py +285 -0
  29. attune/cli/commands/setup.py +96 -0
  30. attune/cli/commands/status.py +235 -0
  31. attune/cli/commands/sync.py +166 -0
  32. attune/cli/commands/tier.py +121 -0
  33. attune/cli/commands/utilities.py +114 -0
  34. attune/cli/commands/workflow.py +579 -0
  35. attune/cli/core.py +32 -0
  36. attune/cli/parsers/__init__.py +68 -0
  37. attune/cli/parsers/batch.py +118 -0
  38. attune/cli/parsers/cache.py +65 -0
  39. attune/cli/parsers/help.py +41 -0
  40. attune/cli/parsers/info.py +26 -0
  41. attune/cli/parsers/inspect.py +66 -0
  42. attune/cli/parsers/metrics.py +42 -0
  43. attune/cli/parsers/orchestrate.py +61 -0
  44. attune/cli/parsers/patterns.py +54 -0
  45. attune/cli/parsers/provider.py +40 -0
  46. attune/cli/parsers/routing.py +110 -0
  47. attune/cli/parsers/setup.py +42 -0
  48. attune/cli/parsers/status.py +47 -0
  49. attune/cli/parsers/sync.py +31 -0
  50. attune/cli/parsers/tier.py +33 -0
  51. attune/cli/parsers/workflow.py +77 -0
  52. attune/cli/utils/__init__.py +1 -0
  53. attune/cli/utils/data.py +242 -0
  54. attune/cli/utils/helpers.py +68 -0
  55. attune/cli_legacy.py +3957 -0
  56. attune/cli_minimal.py +1159 -0
  57. attune/cli_router.py +437 -0
  58. attune/cli_unified.py +814 -0
  59. attune/config/__init__.py +66 -0
  60. attune/config/xml_config.py +286 -0
  61. attune/config.py +545 -0
  62. attune/coordination.py +870 -0
  63. attune/core.py +1511 -0
  64. attune/core_modules/__init__.py +15 -0
  65. attune/cost_tracker.py +626 -0
  66. attune/dashboard/__init__.py +41 -0
  67. attune/dashboard/app.py +512 -0
  68. attune/dashboard/simple_server.py +435 -0
  69. attune/dashboard/standalone_server.py +547 -0
  70. attune/discovery.py +306 -0
  71. attune/emergence.py +306 -0
  72. attune/exceptions.py +123 -0
  73. attune/feedback_loops.py +373 -0
  74. attune/hot_reload/README.md +473 -0
  75. attune/hot_reload/__init__.py +62 -0
  76. attune/hot_reload/config.py +83 -0
  77. attune/hot_reload/integration.py +229 -0
  78. attune/hot_reload/reloader.py +298 -0
  79. attune/hot_reload/watcher.py +183 -0
  80. attune/hot_reload/websocket.py +177 -0
  81. attune/levels.py +577 -0
  82. attune/leverage_points.py +441 -0
  83. attune/logging_config.py +261 -0
  84. attune/mcp/__init__.py +10 -0
  85. attune/mcp/server.py +506 -0
  86. attune/memory/__init__.py +237 -0
  87. attune/memory/claude_memory.py +469 -0
  88. attune/memory/config.py +224 -0
  89. attune/memory/control_panel.py +1290 -0
  90. attune/memory/control_panel_support.py +145 -0
  91. attune/memory/cross_session.py +845 -0
  92. attune/memory/edges.py +179 -0
  93. attune/memory/encryption.py +159 -0
  94. attune/memory/file_session.py +770 -0
  95. attune/memory/graph.py +570 -0
  96. attune/memory/long_term.py +913 -0
  97. attune/memory/long_term_types.py +99 -0
  98. attune/memory/mixins/__init__.py +25 -0
  99. attune/memory/mixins/backend_init_mixin.py +249 -0
  100. attune/memory/mixins/capabilities_mixin.py +208 -0
  101. attune/memory/mixins/handoff_mixin.py +208 -0
  102. attune/memory/mixins/lifecycle_mixin.py +49 -0
  103. attune/memory/mixins/long_term_mixin.py +352 -0
  104. attune/memory/mixins/promotion_mixin.py +109 -0
  105. attune/memory/mixins/short_term_mixin.py +182 -0
  106. attune/memory/nodes.py +179 -0
  107. attune/memory/redis_bootstrap.py +540 -0
  108. attune/memory/security/__init__.py +31 -0
  109. attune/memory/security/audit_logger.py +932 -0
  110. attune/memory/security/pii_scrubber.py +640 -0
  111. attune/memory/security/secrets_detector.py +678 -0
  112. attune/memory/short_term.py +2192 -0
  113. attune/memory/simple_storage.py +302 -0
  114. attune/memory/storage/__init__.py +15 -0
  115. attune/memory/storage_backend.py +167 -0
  116. attune/memory/summary_index.py +583 -0
  117. attune/memory/types.py +446 -0
  118. attune/memory/unified.py +182 -0
  119. attune/meta_workflows/__init__.py +74 -0
  120. attune/meta_workflows/agent_creator.py +248 -0
  121. attune/meta_workflows/builtin_templates.py +567 -0
  122. attune/meta_workflows/cli_commands/__init__.py +56 -0
  123. attune/meta_workflows/cli_commands/agent_commands.py +321 -0
  124. attune/meta_workflows/cli_commands/analytics_commands.py +442 -0
  125. attune/meta_workflows/cli_commands/config_commands.py +232 -0
  126. attune/meta_workflows/cli_commands/memory_commands.py +182 -0
  127. attune/meta_workflows/cli_commands/template_commands.py +354 -0
  128. attune/meta_workflows/cli_commands/workflow_commands.py +382 -0
  129. attune/meta_workflows/cli_meta_workflows.py +59 -0
  130. attune/meta_workflows/form_engine.py +292 -0
  131. attune/meta_workflows/intent_detector.py +409 -0
  132. attune/meta_workflows/models.py +569 -0
  133. attune/meta_workflows/pattern_learner.py +738 -0
  134. attune/meta_workflows/plan_generator.py +384 -0
  135. attune/meta_workflows/session_context.py +397 -0
  136. attune/meta_workflows/template_registry.py +229 -0
  137. attune/meta_workflows/workflow.py +984 -0
  138. attune/metrics/__init__.py +12 -0
  139. attune/metrics/collector.py +31 -0
  140. attune/metrics/prompt_metrics.py +194 -0
  141. attune/models/__init__.py +172 -0
  142. attune/models/__main__.py +13 -0
  143. attune/models/adaptive_routing.py +437 -0
  144. attune/models/auth_cli.py +444 -0
  145. attune/models/auth_strategy.py +450 -0
  146. attune/models/cli.py +655 -0
  147. attune/models/empathy_executor.py +354 -0
  148. attune/models/executor.py +257 -0
  149. attune/models/fallback.py +762 -0
  150. attune/models/provider_config.py +282 -0
  151. attune/models/registry.py +472 -0
  152. attune/models/tasks.py +359 -0
  153. attune/models/telemetry/__init__.py +71 -0
  154. attune/models/telemetry/analytics.py +594 -0
  155. attune/models/telemetry/backend.py +196 -0
  156. attune/models/telemetry/data_models.py +431 -0
  157. attune/models/telemetry/storage.py +489 -0
  158. attune/models/token_estimator.py +420 -0
  159. attune/models/validation.py +280 -0
  160. attune/monitoring/__init__.py +52 -0
  161. attune/monitoring/alerts.py +946 -0
  162. attune/monitoring/alerts_cli.py +448 -0
  163. attune/monitoring/multi_backend.py +271 -0
  164. attune/monitoring/otel_backend.py +362 -0
  165. attune/optimization/__init__.py +19 -0
  166. attune/optimization/context_optimizer.py +272 -0
  167. attune/orchestration/__init__.py +67 -0
  168. attune/orchestration/agent_templates.py +707 -0
  169. attune/orchestration/config_store.py +499 -0
  170. attune/orchestration/execution_strategies.py +2111 -0
  171. attune/orchestration/meta_orchestrator.py +1168 -0
  172. attune/orchestration/pattern_learner.py +696 -0
  173. attune/orchestration/real_tools.py +931 -0
  174. attune/pattern_cache.py +187 -0
  175. attune/pattern_library.py +542 -0
  176. attune/patterns/debugging/all_patterns.json +81 -0
  177. attune/patterns/debugging/workflow_20260107_1770825e.json +77 -0
  178. attune/patterns/refactoring_memory.json +89 -0
  179. attune/persistence.py +564 -0
  180. attune/platform_utils.py +265 -0
  181. attune/plugins/__init__.py +28 -0
  182. attune/plugins/base.py +361 -0
  183. attune/plugins/registry.py +268 -0
  184. attune/project_index/__init__.py +32 -0
  185. attune/project_index/cli.py +335 -0
  186. attune/project_index/index.py +667 -0
  187. attune/project_index/models.py +504 -0
  188. attune/project_index/reports.py +474 -0
  189. attune/project_index/scanner.py +777 -0
  190. attune/project_index/scanner_parallel.py +291 -0
  191. attune/prompts/__init__.py +61 -0
  192. attune/prompts/config.py +77 -0
  193. attune/prompts/context.py +177 -0
  194. attune/prompts/parser.py +285 -0
  195. attune/prompts/registry.py +313 -0
  196. attune/prompts/templates.py +208 -0
  197. attune/redis_config.py +302 -0
  198. attune/redis_memory.py +799 -0
  199. attune/resilience/__init__.py +56 -0
  200. attune/resilience/circuit_breaker.py +256 -0
  201. attune/resilience/fallback.py +179 -0
  202. attune/resilience/health.py +300 -0
  203. attune/resilience/retry.py +209 -0
  204. attune/resilience/timeout.py +135 -0
  205. attune/routing/__init__.py +43 -0
  206. attune/routing/chain_executor.py +433 -0
  207. attune/routing/classifier.py +217 -0
  208. attune/routing/smart_router.py +234 -0
  209. attune/routing/workflow_registry.py +343 -0
  210. attune/scaffolding/README.md +589 -0
  211. attune/scaffolding/__init__.py +35 -0
  212. attune/scaffolding/__main__.py +14 -0
  213. attune/scaffolding/cli.py +240 -0
  214. attune/scaffolding/templates/base_wizard.py.jinja2 +121 -0
  215. attune/scaffolding/templates/coach_wizard.py.jinja2 +321 -0
  216. attune/scaffolding/templates/domain_wizard.py.jinja2 +408 -0
  217. attune/scaffolding/templates/linear_flow_wizard.py.jinja2 +203 -0
  218. attune/socratic/__init__.py +256 -0
  219. attune/socratic/ab_testing.py +958 -0
  220. attune/socratic/blueprint.py +533 -0
  221. attune/socratic/cli.py +703 -0
  222. attune/socratic/collaboration.py +1114 -0
  223. attune/socratic/domain_templates.py +924 -0
  224. attune/socratic/embeddings.py +738 -0
  225. attune/socratic/engine.py +794 -0
  226. attune/socratic/explainer.py +682 -0
  227. attune/socratic/feedback.py +772 -0
  228. attune/socratic/forms.py +629 -0
  229. attune/socratic/generator.py +732 -0
  230. attune/socratic/llm_analyzer.py +637 -0
  231. attune/socratic/mcp_server.py +702 -0
  232. attune/socratic/session.py +312 -0
  233. attune/socratic/storage.py +667 -0
  234. attune/socratic/success.py +730 -0
  235. attune/socratic/visual_editor.py +860 -0
  236. attune/socratic/web_ui.py +958 -0
  237. attune/telemetry/__init__.py +39 -0
  238. attune/telemetry/agent_coordination.py +475 -0
  239. attune/telemetry/agent_tracking.py +367 -0
  240. attune/telemetry/approval_gates.py +545 -0
  241. attune/telemetry/cli.py +1231 -0
  242. attune/telemetry/commands/__init__.py +14 -0
  243. attune/telemetry/commands/dashboard_commands.py +696 -0
  244. attune/telemetry/event_streaming.py +409 -0
  245. attune/telemetry/feedback_loop.py +567 -0
  246. attune/telemetry/usage_tracker.py +591 -0
  247. attune/templates.py +754 -0
  248. attune/test_generator/__init__.py +38 -0
  249. attune/test_generator/__main__.py +14 -0
  250. attune/test_generator/cli.py +234 -0
  251. attune/test_generator/generator.py +355 -0
  252. attune/test_generator/risk_analyzer.py +216 -0
  253. attune/test_generator/templates/unit_test.py.jinja2 +272 -0
  254. attune/tier_recommender.py +384 -0
  255. attune/tools.py +183 -0
  256. attune/trust/__init__.py +28 -0
  257. attune/trust/circuit_breaker.py +579 -0
  258. attune/trust_building.py +527 -0
  259. attune/validation/__init__.py +19 -0
  260. attune/validation/xml_validator.py +281 -0
  261. attune/vscode_bridge.py +173 -0
  262. attune/workflow_commands.py +780 -0
  263. attune/workflow_patterns/__init__.py +33 -0
  264. attune/workflow_patterns/behavior.py +249 -0
  265. attune/workflow_patterns/core.py +76 -0
  266. attune/workflow_patterns/output.py +99 -0
  267. attune/workflow_patterns/registry.py +255 -0
  268. attune/workflow_patterns/structural.py +288 -0
  269. attune/workflows/__init__.py +539 -0
  270. attune/workflows/autonomous_test_gen.py +1268 -0
  271. attune/workflows/base.py +2667 -0
  272. attune/workflows/batch_processing.py +342 -0
  273. attune/workflows/bug_predict.py +1084 -0
  274. attune/workflows/builder.py +273 -0
  275. attune/workflows/caching.py +253 -0
  276. attune/workflows/code_review.py +1048 -0
  277. attune/workflows/code_review_adapters.py +312 -0
  278. attune/workflows/code_review_pipeline.py +722 -0
  279. attune/workflows/config.py +645 -0
  280. attune/workflows/dependency_check.py +644 -0
  281. attune/workflows/document_gen/__init__.py +25 -0
  282. attune/workflows/document_gen/config.py +30 -0
  283. attune/workflows/document_gen/report_formatter.py +162 -0
  284. attune/workflows/document_gen/workflow.py +1426 -0
  285. attune/workflows/document_manager.py +216 -0
  286. attune/workflows/document_manager_README.md +134 -0
  287. attune/workflows/documentation_orchestrator.py +1205 -0
  288. attune/workflows/history.py +510 -0
  289. attune/workflows/keyboard_shortcuts/__init__.py +39 -0
  290. attune/workflows/keyboard_shortcuts/generators.py +391 -0
  291. attune/workflows/keyboard_shortcuts/parsers.py +416 -0
  292. attune/workflows/keyboard_shortcuts/prompts.py +295 -0
  293. attune/workflows/keyboard_shortcuts/schema.py +193 -0
  294. attune/workflows/keyboard_shortcuts/workflow.py +509 -0
  295. attune/workflows/llm_base.py +363 -0
  296. attune/workflows/manage_docs.py +87 -0
  297. attune/workflows/manage_docs_README.md +134 -0
  298. attune/workflows/manage_documentation.py +821 -0
  299. attune/workflows/new_sample_workflow1.py +149 -0
  300. attune/workflows/new_sample_workflow1_README.md +150 -0
  301. attune/workflows/orchestrated_health_check.py +849 -0
  302. attune/workflows/orchestrated_release_prep.py +600 -0
  303. attune/workflows/output.py +413 -0
  304. attune/workflows/perf_audit.py +863 -0
  305. attune/workflows/pr_review.py +762 -0
  306. attune/workflows/progress.py +785 -0
  307. attune/workflows/progress_server.py +322 -0
  308. attune/workflows/progressive/README 2.md +454 -0
  309. attune/workflows/progressive/README.md +454 -0
  310. attune/workflows/progressive/__init__.py +82 -0
  311. attune/workflows/progressive/cli.py +219 -0
  312. attune/workflows/progressive/core.py +488 -0
  313. attune/workflows/progressive/orchestrator.py +723 -0
  314. attune/workflows/progressive/reports.py +520 -0
  315. attune/workflows/progressive/telemetry.py +274 -0
  316. attune/workflows/progressive/test_gen.py +495 -0
  317. attune/workflows/progressive/workflow.py +589 -0
  318. attune/workflows/refactor_plan.py +694 -0
  319. attune/workflows/release_prep.py +895 -0
  320. attune/workflows/release_prep_crew.py +969 -0
  321. attune/workflows/research_synthesis.py +404 -0
  322. attune/workflows/routing.py +168 -0
  323. attune/workflows/secure_release.py +593 -0
  324. attune/workflows/security_adapters.py +297 -0
  325. attune/workflows/security_audit.py +1329 -0
  326. attune/workflows/security_audit_phase3.py +355 -0
  327. attune/workflows/seo_optimization.py +633 -0
  328. attune/workflows/step_config.py +234 -0
  329. attune/workflows/telemetry_mixin.py +269 -0
  330. attune/workflows/test5.py +125 -0
  331. attune/workflows/test5_README.md +158 -0
  332. attune/workflows/test_coverage_boost_crew.py +849 -0
  333. attune/workflows/test_gen/__init__.py +52 -0
  334. attune/workflows/test_gen/ast_analyzer.py +249 -0
  335. attune/workflows/test_gen/config.py +88 -0
  336. attune/workflows/test_gen/data_models.py +38 -0
  337. attune/workflows/test_gen/report_formatter.py +289 -0
  338. attune/workflows/test_gen/test_templates.py +381 -0
  339. attune/workflows/test_gen/workflow.py +655 -0
  340. attune/workflows/test_gen.py +54 -0
  341. attune/workflows/test_gen_behavioral.py +477 -0
  342. attune/workflows/test_gen_parallel.py +341 -0
  343. attune/workflows/test_lifecycle.py +526 -0
  344. attune/workflows/test_maintenance.py +627 -0
  345. attune/workflows/test_maintenance_cli.py +590 -0
  346. attune/workflows/test_maintenance_crew.py +840 -0
  347. attune/workflows/test_runner.py +622 -0
  348. attune/workflows/tier_tracking.py +531 -0
  349. attune/workflows/xml_enhanced_crew.py +285 -0
  350. attune_ai-2.0.0.dist-info/METADATA +1026 -0
  351. attune_ai-2.0.0.dist-info/RECORD +457 -0
  352. attune_ai-2.0.0.dist-info/WHEEL +5 -0
  353. attune_ai-2.0.0.dist-info/entry_points.txt +26 -0
  354. attune_ai-2.0.0.dist-info/licenses/LICENSE +201 -0
  355. attune_ai-2.0.0.dist-info/licenses/LICENSE_CHANGE_ANNOUNCEMENT.md +101 -0
  356. attune_ai-2.0.0.dist-info/top_level.txt +5 -0
  357. attune_healthcare/__init__.py +13 -0
  358. attune_healthcare/monitors/__init__.py +9 -0
  359. attune_healthcare/monitors/clinical_protocol_monitor.py +315 -0
  360. attune_healthcare/monitors/monitoring/__init__.py +44 -0
  361. attune_healthcare/monitors/monitoring/protocol_checker.py +300 -0
  362. attune_healthcare/monitors/monitoring/protocol_loader.py +214 -0
  363. attune_healthcare/monitors/monitoring/sensor_parsers.py +306 -0
  364. attune_healthcare/monitors/monitoring/trajectory_analyzer.py +389 -0
  365. attune_llm/README.md +553 -0
  366. attune_llm/__init__.py +28 -0
  367. attune_llm/agent_factory/__init__.py +53 -0
  368. attune_llm/agent_factory/adapters/__init__.py +85 -0
  369. attune_llm/agent_factory/adapters/autogen_adapter.py +312 -0
  370. attune_llm/agent_factory/adapters/crewai_adapter.py +483 -0
  371. attune_llm/agent_factory/adapters/haystack_adapter.py +298 -0
  372. attune_llm/agent_factory/adapters/langchain_adapter.py +362 -0
  373. attune_llm/agent_factory/adapters/langgraph_adapter.py +333 -0
  374. attune_llm/agent_factory/adapters/native.py +228 -0
  375. attune_llm/agent_factory/adapters/wizard_adapter.py +423 -0
  376. attune_llm/agent_factory/base.py +305 -0
  377. attune_llm/agent_factory/crews/__init__.py +67 -0
  378. attune_llm/agent_factory/crews/code_review.py +1113 -0
  379. attune_llm/agent_factory/crews/health_check.py +1262 -0
  380. attune_llm/agent_factory/crews/refactoring.py +1128 -0
  381. attune_llm/agent_factory/crews/security_audit.py +1018 -0
  382. attune_llm/agent_factory/decorators.py +287 -0
  383. attune_llm/agent_factory/factory.py +558 -0
  384. attune_llm/agent_factory/framework.py +193 -0
  385. attune_llm/agent_factory/memory_integration.py +328 -0
  386. attune_llm/agent_factory/resilient.py +320 -0
  387. attune_llm/agents_md/__init__.py +22 -0
  388. attune_llm/agents_md/loader.py +218 -0
  389. attune_llm/agents_md/parser.py +271 -0
  390. attune_llm/agents_md/registry.py +307 -0
  391. attune_llm/claude_memory.py +466 -0
  392. attune_llm/cli/__init__.py +8 -0
  393. attune_llm/cli/sync_claude.py +487 -0
  394. attune_llm/code_health.py +1313 -0
  395. attune_llm/commands/__init__.py +51 -0
  396. attune_llm/commands/context.py +375 -0
  397. attune_llm/commands/loader.py +301 -0
  398. attune_llm/commands/models.py +231 -0
  399. attune_llm/commands/parser.py +371 -0
  400. attune_llm/commands/registry.py +429 -0
  401. attune_llm/config/__init__.py +29 -0
  402. attune_llm/config/unified.py +291 -0
  403. attune_llm/context/__init__.py +22 -0
  404. attune_llm/context/compaction.py +455 -0
  405. attune_llm/context/manager.py +434 -0
  406. attune_llm/contextual_patterns.py +361 -0
  407. attune_llm/core.py +907 -0
  408. attune_llm/git_pattern_extractor.py +435 -0
  409. attune_llm/hooks/__init__.py +24 -0
  410. attune_llm/hooks/config.py +306 -0
  411. attune_llm/hooks/executor.py +289 -0
  412. attune_llm/hooks/registry.py +302 -0
  413. attune_llm/hooks/scripts/__init__.py +39 -0
  414. attune_llm/hooks/scripts/evaluate_session.py +201 -0
  415. attune_llm/hooks/scripts/first_time_init.py +285 -0
  416. attune_llm/hooks/scripts/pre_compact.py +207 -0
  417. attune_llm/hooks/scripts/session_end.py +183 -0
  418. attune_llm/hooks/scripts/session_start.py +163 -0
  419. attune_llm/hooks/scripts/suggest_compact.py +225 -0
  420. attune_llm/learning/__init__.py +30 -0
  421. attune_llm/learning/evaluator.py +438 -0
  422. attune_llm/learning/extractor.py +514 -0
  423. attune_llm/learning/storage.py +560 -0
  424. attune_llm/levels.py +227 -0
  425. attune_llm/pattern_confidence.py +414 -0
  426. attune_llm/pattern_resolver.py +272 -0
  427. attune_llm/pattern_summary.py +350 -0
  428. attune_llm/providers.py +967 -0
  429. attune_llm/routing/__init__.py +32 -0
  430. attune_llm/routing/model_router.py +362 -0
  431. attune_llm/security/IMPLEMENTATION_SUMMARY.md +413 -0
  432. attune_llm/security/PHASE2_COMPLETE.md +384 -0
  433. attune_llm/security/PHASE2_SECRETS_DETECTOR_COMPLETE.md +271 -0
  434. attune_llm/security/QUICK_REFERENCE.md +316 -0
  435. attune_llm/security/README.md +262 -0
  436. attune_llm/security/__init__.py +62 -0
  437. attune_llm/security/audit_logger.py +929 -0
  438. attune_llm/security/audit_logger_example.py +152 -0
  439. attune_llm/security/pii_scrubber.py +640 -0
  440. attune_llm/security/secrets_detector.py +678 -0
  441. attune_llm/security/secrets_detector_example.py +304 -0
  442. attune_llm/security/secure_memdocs.py +1192 -0
  443. attune_llm/security/secure_memdocs_example.py +278 -0
  444. attune_llm/session_status.py +745 -0
  445. attune_llm/state.py +246 -0
  446. attune_llm/utils/__init__.py +5 -0
  447. attune_llm/utils/tokens.py +349 -0
  448. attune_software/SOFTWARE_PLUGIN_README.md +57 -0
  449. attune_software/__init__.py +13 -0
  450. attune_software/cli/__init__.py +120 -0
  451. attune_software/cli/inspect.py +362 -0
  452. attune_software/cli.py +574 -0
  453. attune_software/plugin.py +188 -0
  454. workflow_scaffolding/__init__.py +11 -0
  455. workflow_scaffolding/__main__.py +12 -0
  456. workflow_scaffolding/cli.py +206 -0
  457. workflow_scaffolding/generator.py +265 -0
@@ -0,0 +1,268 @@
1
+ """Empathy Framework - Plugin Registry
2
+
3
+ Auto-discovery and management of domain plugins.
4
+
5
+ Copyright 2025 Smart AI Memory, LLC
6
+ Licensed under Fair Source 0.9
7
+ """
8
+
9
+ import logging
10
+ from importlib.metadata import entry_points
11
+
12
+ from .base import BasePlugin, BaseWorkflow, PluginValidationError
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class PluginRegistry:
18
+ """Central registry for managing domain plugins.
19
+
20
+ Features:
21
+ - Auto-discovery via entry points
22
+ - Manual registration
23
+ - Lazy initialization
24
+ - Graceful degradation (missing plugins don't crash)
25
+ """
26
+
27
+ def __init__(self):
28
+ self._plugins: dict[str, BasePlugin] = {}
29
+ self._auto_discovered = False
30
+ self.logger = logging.getLogger("empathy.plugins.registry")
31
+
32
+ def auto_discover(self) -> None:
33
+ """Automatically discover plugins via entry points.
34
+
35
+ Plugins register themselves in setup.py/pyproject.toml:
36
+
37
+ [project.entry-points."empathy_framework.plugins"]
38
+ software = "empathy_software.plugin:SoftwarePlugin"
39
+ healthcare = "empathy_healthcare.plugin:HealthcarePlugin"
40
+ """
41
+ if self._auto_discovered:
42
+ return
43
+
44
+ self.logger.info("Auto-discovering plugins...")
45
+
46
+ # Python 3.10+ has modern entry_points API with group parameter
47
+ discovered = entry_points(group="empathy_framework.plugins")
48
+
49
+ for ep in discovered:
50
+ try:
51
+ self.logger.info(f"Loading plugin '{ep.name}' from entry point")
52
+ plugin_class = ep.load()
53
+ plugin_instance = plugin_class()
54
+ self.register_plugin(ep.name, plugin_instance)
55
+ self.logger.info(f"Successfully loaded plugin: {ep.name}")
56
+ except Exception as e:
57
+ # Graceful degradation: log but don't crash
58
+ self.logger.warning(f"Failed to load plugin '{ep.name}': {e}", exc_info=True)
59
+
60
+ self._auto_discovered = True
61
+ self.logger.info(f"Auto-discovery complete. {len(self._plugins)} plugins loaded.")
62
+
63
+ def register_plugin(self, name: str, plugin: BasePlugin) -> None:
64
+ """Manually register a plugin.
65
+
66
+ Args:
67
+ name: Plugin identifier (e.g., 'software', 'healthcare')
68
+ plugin: Plugin instance
69
+
70
+ Raises:
71
+ PluginValidationError: If plugin is invalid
72
+
73
+ """
74
+ # Validate plugin
75
+ try:
76
+ metadata = plugin.get_metadata()
77
+ if not metadata.name:
78
+ raise PluginValidationError("Plugin metadata missing 'name'")
79
+ if not metadata.domain:
80
+ raise PluginValidationError("Plugin metadata missing 'domain'")
81
+ except Exception as e:
82
+ raise PluginValidationError(f"Invalid plugin metadata: {e}") from e
83
+
84
+ # Register
85
+ self._plugins[name] = plugin
86
+ self.logger.info(
87
+ f"Registered plugin '{name}' (domain: {metadata.domain}, version: {metadata.version})",
88
+ )
89
+
90
+ def get_plugin(self, name: str) -> BasePlugin | None:
91
+ """Get a plugin by name.
92
+
93
+ Args:
94
+ name: Plugin identifier
95
+
96
+ Returns:
97
+ Plugin instance or None if not found
98
+
99
+ """
100
+ if not self._auto_discovered:
101
+ self.auto_discover()
102
+
103
+ plugin = self._plugins.get(name)
104
+ if plugin and not plugin._initialized:
105
+ plugin.initialize()
106
+
107
+ return plugin
108
+
109
+ def list_plugins(self) -> list[str]:
110
+ """List all registered plugin names.
111
+
112
+ Returns:
113
+ List of plugin identifiers
114
+
115
+ """
116
+ if not self._auto_discovered:
117
+ self.auto_discover()
118
+
119
+ return list(self._plugins.keys())
120
+
121
+ def list_all_workflows(self) -> dict[str, list[str]]:
122
+ """List all workflows from all plugins.
123
+
124
+ Returns:
125
+ Dictionary mapping plugin_name -> list of workflow_ids
126
+
127
+ """
128
+ if not self._auto_discovered:
129
+ self.auto_discover()
130
+
131
+ result = {}
132
+ for plugin_name, plugin in self._plugins.items():
133
+ result[plugin_name] = plugin.list_workflows()
134
+
135
+ return result
136
+
137
+ def get_workflow(self, plugin_name: str, workflow_id: str) -> type[BaseWorkflow] | None:
138
+ """Get a workflow from a specific plugin.
139
+
140
+ Args:
141
+ plugin_name: Plugin identifier
142
+ workflow_id: Workflow identifier within plugin
143
+
144
+ Returns:
145
+ Workflow class or None if not found
146
+
147
+ """
148
+ plugin = self.get_plugin(plugin_name)
149
+ if not plugin:
150
+ self.logger.warning(f"Plugin '{plugin_name}' not found")
151
+ return None
152
+
153
+ return plugin.get_workflow(workflow_id)
154
+
155
+ def get_workflow_info(self, plugin_name: str, workflow_id: str) -> dict | None:
156
+ """Get information about a workflow.
157
+
158
+ Args:
159
+ plugin_name: Plugin identifier
160
+ workflow_id: Workflow identifier
161
+
162
+ Returns:
163
+ Dictionary with workflow metadata or None
164
+
165
+ """
166
+ plugin = self.get_plugin(plugin_name)
167
+ if not plugin:
168
+ return None
169
+
170
+ return plugin.get_workflow_info(workflow_id)
171
+
172
+ def find_workflows_by_level(self, empathy_level: int) -> list[dict]:
173
+ """Find all workflows operating at a specific empathy level.
174
+
175
+ Args:
176
+ empathy_level: Target empathy level (1-5)
177
+
178
+ Returns:
179
+ List of workflow info dictionaries
180
+
181
+ """
182
+ if not self._auto_discovered:
183
+ self.auto_discover()
184
+
185
+ results = []
186
+ for plugin_name, plugin in self._plugins.items():
187
+ for workflow_id in plugin.list_workflows():
188
+ info = plugin.get_workflow_info(workflow_id)
189
+ if info and info.get("empathy_level") == empathy_level:
190
+ info["plugin"] = plugin_name
191
+ results.append(info)
192
+
193
+ return results
194
+
195
+ def find_workflows_by_domain(self, domain: str) -> list[dict]:
196
+ """Find all workflows for a specific domain.
197
+
198
+ Args:
199
+ domain: Domain identifier (e.g., 'software', 'healthcare')
200
+
201
+ Returns:
202
+ List of workflow info dictionaries
203
+
204
+ """
205
+ if not self._auto_discovered:
206
+ self.auto_discover()
207
+
208
+ results = []
209
+ for plugin_name, plugin in self._plugins.items():
210
+ metadata = plugin.get_metadata()
211
+ if metadata.domain == domain:
212
+ for workflow_id in plugin.list_workflows():
213
+ info = plugin.get_workflow_info(workflow_id)
214
+ if info:
215
+ info["plugin"] = plugin_name
216
+ results.append(info)
217
+
218
+ return results
219
+
220
+ def get_statistics(self) -> dict:
221
+ """Get registry statistics.
222
+
223
+ Returns:
224
+ Dictionary with counts and metadata
225
+
226
+ """
227
+ if not self._auto_discovered:
228
+ self.auto_discover()
229
+
230
+ total_workflows = sum(len(plugin.list_workflows()) for plugin in self._plugins.values())
231
+
232
+ # Count workflows by level
233
+ workflows_by_level = {}
234
+ for level in range(1, 6):
235
+ workflows_by_level[f"level_{level}"] = len(self.find_workflows_by_level(level))
236
+
237
+ return {
238
+ "total_plugins": len(self._plugins),
239
+ "total_workflows": total_workflows,
240
+ "plugins": [
241
+ {
242
+ "name": name,
243
+ "domain": plugin.get_metadata().domain,
244
+ "version": plugin.get_metadata().version,
245
+ "workflow_count": len(plugin.list_workflows()),
246
+ }
247
+ for name, plugin in self._plugins.items()
248
+ ],
249
+ "workflows_by_level": workflows_by_level,
250
+ }
251
+
252
+
253
+ # Global registry instance
254
+ _global_registry: PluginRegistry | None = None
255
+
256
+
257
+ def get_global_registry() -> PluginRegistry:
258
+ """Get the global plugin registry instance (singleton).
259
+
260
+ Returns:
261
+ Global PluginRegistry instance
262
+
263
+ """
264
+ global _global_registry
265
+ if _global_registry is None:
266
+ _global_registry = PluginRegistry()
267
+ _global_registry.auto_discover()
268
+ return _global_registry
@@ -0,0 +1,32 @@
1
+ """Project Index - Codebase Intelligence Layer
2
+
3
+ Tracks metadata about all files in a project to enable:
4
+ - Test coverage gap analysis
5
+ - Staleness detection (code changed, tests didn't)
6
+ - Dependency mapping
7
+ - Project health reports
8
+ - Workflow intelligence
9
+
10
+ Storage:
11
+ - Primary: .attune/project_index.json
12
+ - Real-time: Redis (when short-term memory enabled)
13
+
14
+ Copyright 2025 Smart AI Memory, LLC
15
+ Licensed under Fair Source 0.9
16
+ """
17
+
18
+ from .index import ProjectIndex
19
+ from .models import FileRecord, IndexConfig, ProjectSummary
20
+ from .reports import ReportGenerator
21
+ from .scanner import ProjectScanner
22
+ from .scanner_parallel import ParallelProjectScanner
23
+
24
+ __all__ = [
25
+ "FileRecord",
26
+ "IndexConfig",
27
+ "ParallelProjectScanner",
28
+ "ProjectIndex",
29
+ "ProjectScanner",
30
+ "ProjectSummary",
31
+ "ReportGenerator",
32
+ ]
@@ -0,0 +1,335 @@
1
+ """CLI for Project Index
2
+
3
+ Commands for generating, querying, and reporting on the project index.
4
+
5
+ Usage:
6
+ python -m attune.project_index.cli refresh
7
+ python -m attune.project_index.cli report health
8
+ python -m attune.project_index.cli query needing_tests
9
+ python -m attune.project_index.cli summary
10
+
11
+ Copyright 2025 Smart AI Memory, LLC
12
+ Licensed under Fair Source 0.9
13
+ """
14
+
15
+ import argparse
16
+ import json
17
+ import sys
18
+ from pathlib import Path
19
+
20
+ from .index import ProjectIndex
21
+ from .reports import ReportGenerator
22
+
23
+
24
+ def main() -> int:
25
+ """Main CLI entry point."""
26
+ parser = argparse.ArgumentParser(
27
+ description="Project Index - Codebase Intelligence",
28
+ formatter_class=argparse.RawDescriptionHelpFormatter,
29
+ epilog="""
30
+ Examples:
31
+ # Generate/refresh the index
32
+ python -m attune.project_index.cli refresh
33
+
34
+ # View project summary
35
+ python -m attune.project_index.cli summary
36
+
37
+ # Generate health report
38
+ python -m attune.project_index.cli report health
39
+
40
+ # Find files needing tests
41
+ python -m attune.project_index.cli query needing_tests
42
+
43
+ # Find stale test files
44
+ python -m attune.project_index.cli query stale
45
+ """,
46
+ )
47
+
48
+ parser.add_argument(
49
+ "--project",
50
+ "-p",
51
+ default=".",
52
+ help="Project root directory (default: current directory)",
53
+ )
54
+
55
+ parser.add_argument(
56
+ "--json",
57
+ "-j",
58
+ action="store_true",
59
+ help="Output in JSON format",
60
+ )
61
+
62
+ subparsers = parser.add_subparsers(dest="command", help="Command to run")
63
+
64
+ # refresh command
65
+ refresh_parser = subparsers.add_parser("refresh", help="Refresh the project index")
66
+ refresh_parser.add_argument(
67
+ "--force",
68
+ "-f",
69
+ action="store_true",
70
+ help="Force full re-scan even if index exists",
71
+ )
72
+
73
+ # summary command
74
+ subparsers.add_parser("summary", help="Show project summary")
75
+
76
+ # report command
77
+ report_parser = subparsers.add_parser("report", help="Generate a report")
78
+ report_parser.add_argument(
79
+ "report_type",
80
+ choices=["health", "test_gap", "staleness", "coverage", "sprint"],
81
+ help="Type of report to generate",
82
+ )
83
+ report_parser.add_argument(
84
+ "--markdown",
85
+ "-m",
86
+ action="store_true",
87
+ help="Output in markdown format",
88
+ )
89
+
90
+ # query command
91
+ query_parser = subparsers.add_parser("query", help="Query the index")
92
+ query_parser.add_argument(
93
+ "query_type",
94
+ choices=["needing_tests", "stale", "high_impact", "attention", "all"],
95
+ help="Type of query",
96
+ )
97
+ query_parser.add_argument(
98
+ "--limit",
99
+ "-l",
100
+ type=int,
101
+ default=20,
102
+ help="Maximum number of results",
103
+ )
104
+
105
+ # file command
106
+ file_parser = subparsers.add_parser("file", help="Get info about a specific file")
107
+ file_parser.add_argument("path", help="Path to the file")
108
+
109
+ args = parser.parse_args()
110
+
111
+ if not args.command:
112
+ parser.print_help()
113
+ return 1
114
+
115
+ # Initialize index
116
+ project_root = Path(args.project).resolve()
117
+ index = ProjectIndex(str(project_root))
118
+
119
+ # Execute command
120
+ if args.command == "refresh":
121
+ return cmd_refresh(index, args)
122
+ if args.command == "summary":
123
+ return cmd_summary(index, args)
124
+ if args.command == "report":
125
+ return cmd_report(index, args)
126
+ if args.command == "query":
127
+ return cmd_query(index, args)
128
+ if args.command == "file":
129
+ return cmd_file(index, args)
130
+
131
+ return 0
132
+
133
+
134
+ def cmd_refresh(index: ProjectIndex, args: argparse.Namespace) -> int:
135
+ """Refresh the index."""
136
+ print(f"Refreshing index for: {index.project_root}")
137
+ print()
138
+
139
+ index.refresh()
140
+
141
+ summary = index.get_summary()
142
+
143
+ print("Index refreshed successfully!")
144
+ print(f" Files indexed: {summary.total_files}")
145
+ print(f" Source files: {summary.source_files}")
146
+ print(f" Test files: {summary.test_files}")
147
+ print(f" Files needing tests: {summary.files_without_tests}")
148
+ print(f" Files needing attention: {summary.files_needing_attention}")
149
+ print()
150
+ print(f"Index saved to: {index._index_path}")
151
+
152
+ return 0
153
+
154
+
155
+ def cmd_summary(index: ProjectIndex, args: argparse.Namespace) -> int:
156
+ """Show project summary."""
157
+ if not index.load():
158
+ print("No index found. Run 'refresh' first.")
159
+ return 1
160
+
161
+ summary = index.get_summary()
162
+
163
+ if args.json:
164
+ print(json.dumps(summary.to_dict(), indent=2))
165
+ return 0
166
+
167
+ print("=" * 60)
168
+ print("PROJECT INDEX SUMMARY")
169
+ print("=" * 60)
170
+ print()
171
+ print("FILE COUNTS")
172
+ print(f" Total files: {summary.total_files}")
173
+ print(f" Source files: {summary.source_files}")
174
+ print(f" Test files: {summary.test_files}")
175
+ print(f" Config files: {summary.config_files}")
176
+ print(f" Doc files: {summary.doc_files}")
177
+ print()
178
+ print("TEST HEALTH")
179
+ print(f" Files requiring tests: {summary.files_requiring_tests}")
180
+ print(f" Files with tests: {summary.files_with_tests}")
181
+ print(f" Files WITHOUT tests: {summary.files_without_tests}")
182
+ print(f" Average coverage: {summary.test_coverage_avg:.1f}%")
183
+ print(f" Test-to-code ratio: {summary.test_to_code_ratio:.2f}")
184
+ print()
185
+ print("ATTENTION NEEDED")
186
+ print(f" Stale test count: {summary.stale_file_count}")
187
+ print(f" Files need attention: {summary.files_needing_attention}")
188
+ print()
189
+
190
+ if summary.critical_untested_files:
191
+ print("CRITICAL UNTESTED FILES (high impact)")
192
+ for f in summary.critical_untested_files[:5]:
193
+ print(f" - {f}")
194
+ print()
195
+
196
+ return 0
197
+
198
+
199
+ def cmd_report(index: ProjectIndex, args: argparse.Namespace) -> int:
200
+ """Generate a report."""
201
+ if not index.load():
202
+ print("No index found. Run 'refresh' first.")
203
+ return 1
204
+
205
+ generator = ReportGenerator(index.get_summary(), index.get_all_files())
206
+
207
+ report_type = args.report_type
208
+ if report_type == "sprint":
209
+ report_type = "sprint_planning"
210
+
211
+ # Generate report
212
+ if args.markdown:
213
+ print(generator.to_markdown(report_type))
214
+ elif args.json:
215
+ if report_type == "health":
216
+ print(json.dumps(generator.health_report(), indent=2))
217
+ elif report_type == "test_gap":
218
+ print(json.dumps(generator.test_gap_report(), indent=2))
219
+ elif report_type == "staleness":
220
+ print(json.dumps(generator.staleness_report(), indent=2))
221
+ elif report_type == "coverage":
222
+ print(json.dumps(generator.coverage_report(), indent=2))
223
+ elif report_type == "sprint_planning":
224
+ print(json.dumps(generator.sprint_planning_report(), indent=2))
225
+ else:
226
+ # Human-readable format
227
+ print(generator.to_markdown(report_type))
228
+
229
+ return 0
230
+
231
+
232
+ def cmd_query(index: ProjectIndex, args: argparse.Namespace) -> int:
233
+ """Query the index."""
234
+ if not index.load():
235
+ print("No index found. Run 'refresh' first.")
236
+ return 1
237
+
238
+ query_type = args.query_type
239
+ limit = args.limit
240
+
241
+ # Execute query
242
+ if query_type == "needing_tests":
243
+ files = index.get_files_needing_tests()[:limit]
244
+ title = "FILES NEEDING TESTS"
245
+ elif query_type == "stale":
246
+ files = index.get_stale_files()[:limit]
247
+ title = "STALE TEST FILES"
248
+ elif query_type == "high_impact":
249
+ files = index.get_high_impact_files()[:limit]
250
+ title = "HIGH IMPACT FILES"
251
+ elif query_type == "attention":
252
+ files = index.get_files_needing_attention()[:limit]
253
+ title = "FILES NEEDING ATTENTION"
254
+ elif query_type == "all":
255
+ files = index.get_all_files()[:limit]
256
+ title = "ALL FILES"
257
+ else:
258
+ files = []
259
+ title = "RESULTS"
260
+
261
+ if args.json:
262
+ print(json.dumps([f.to_dict() for f in files], indent=2))
263
+ return 0
264
+
265
+ print(f"\n{title} ({len(files)} results)")
266
+ print("=" * 60)
267
+
268
+ for f in files:
269
+ print(f"\n{f.path}")
270
+ print(f" Category: {f.category.value}")
271
+ print(f" Impact Score: {f.impact_score:.1f}")
272
+ print(f" Has Tests: {f.tests_exist}")
273
+ print(f" Coverage: {f.coverage_percent:.1f}%")
274
+ if f.is_stale:
275
+ print(f" STALE: {f.staleness_days} days")
276
+ if f.attention_reasons:
277
+ print(f" Attention: {', '.join(f.attention_reasons)}")
278
+
279
+ print()
280
+ return 0
281
+
282
+
283
+ def cmd_file(index: ProjectIndex, args: argparse.Namespace) -> int:
284
+ """Get info about a specific file."""
285
+ if not index.load():
286
+ print("No index found. Run 'refresh' first.")
287
+ return 1
288
+
289
+ record = index.get_file(args.path)
290
+
291
+ if not record:
292
+ print(f"File not found in index: {args.path}")
293
+ return 1
294
+
295
+ if args.json:
296
+ print(json.dumps(record.to_dict(), indent=2))
297
+ return 0
298
+
299
+ print(f"\nFILE: {record.path}")
300
+ print("=" * 60)
301
+ print(f" Name: {record.name}")
302
+ print(f" Category: {record.category.value}")
303
+ print(f" Language: {record.language}")
304
+ print()
305
+ print("TESTING")
306
+ print(f" Requires Tests: {record.test_requirement.value}")
307
+ print(f" Has Tests: {record.tests_exist}")
308
+ print(f" Test File: {record.test_file_path or 'None'}")
309
+ print(f" Coverage: {record.coverage_percent:.1f}%")
310
+ print(f" Test Count: {record.test_count}")
311
+ print()
312
+ print("METRICS")
313
+ print(f" Lines of Code: {record.lines_of_code}")
314
+ print(f" Complexity: {record.complexity_score:.1f}")
315
+ print(f" Has Docstrings: {record.has_docstrings}")
316
+ print(f" Has Type Hints: {record.has_type_hints}")
317
+ print()
318
+ print("DEPENDENCIES")
319
+ print(f" Imports: {record.import_count} modules")
320
+ print(f" Imported By: {record.imported_by_count} files")
321
+ print(f" Impact Score: {record.impact_score:.1f}")
322
+ print()
323
+ print("STATUS")
324
+ print(f" Needs Attention: {record.needs_attention}")
325
+ if record.attention_reasons:
326
+ print(f" Reasons: {', '.join(record.attention_reasons)}")
327
+ if record.is_stale:
328
+ print(f" STALE: {record.staleness_days} days")
329
+ print()
330
+
331
+ return 0
332
+
333
+
334
+ if __name__ == "__main__":
335
+ sys.exit(main())