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,306 @@
1
+ """Hook Configuration Models
2
+
3
+ Pydantic models for hook system configuration.
4
+ Based on everything-claude-code hooks.json pattern.
5
+
6
+ Copyright 2025 Smart-AI-Memory
7
+ Licensed under Fair Source License 0.9
8
+ """
9
+
10
+ from enum import Enum
11
+ from typing import Any
12
+
13
+ from pydantic import BaseModel, Field
14
+
15
+
16
+ class HookEvent(str, Enum):
17
+ """Hook event types matching Claude Code lifecycle."""
18
+
19
+ PRE_TOOL_USE = "PreToolUse"
20
+ POST_TOOL_USE = "PostToolUse"
21
+ SESSION_START = "SessionStart"
22
+ SESSION_END = "SessionEnd"
23
+ PRE_COMPACT = "PreCompact"
24
+ POST_COMPACT = "PostCompact"
25
+ PRE_COMMAND = "PreCommand"
26
+ POST_COMMAND = "PostCommand"
27
+ STOP = "Stop"
28
+
29
+
30
+ class HookType(str, Enum):
31
+ """Type of hook action."""
32
+
33
+ COMMAND = "command" # Run a command/script
34
+ PYTHON = "python" # Run a Python function
35
+ WEBHOOK = "webhook" # Call a webhook URL
36
+
37
+
38
+ class HookDefinition(BaseModel):
39
+ """Definition of a single hook action.
40
+
41
+ Example:
42
+ hook = HookDefinition(
43
+ type=HookType.PYTHON,
44
+ command="attune_llm.hooks.scripts.session_start:main",
45
+ description="Load previous context on session start"
46
+ )
47
+ """
48
+
49
+ type: HookType = Field(
50
+ default=HookType.PYTHON,
51
+ description="Type of hook action",
52
+ )
53
+ command: str = Field(
54
+ ...,
55
+ description="Command to run, Python module:function path, or webhook URL",
56
+ )
57
+ description: str = Field(
58
+ default="",
59
+ description="Human-readable description of what this hook does",
60
+ )
61
+ timeout: int = Field(
62
+ default=30,
63
+ ge=1,
64
+ le=300,
65
+ description="Timeout in seconds for hook execution",
66
+ )
67
+ async_execution: bool = Field(
68
+ default=False,
69
+ description="Run hook asynchronously (don't wait for completion)",
70
+ )
71
+ on_error: str = Field(
72
+ default="log",
73
+ description="Error handling: 'log', 'raise', or 'ignore'",
74
+ )
75
+
76
+
77
+ class HookMatcher(BaseModel):
78
+ """Matcher for determining when a hook should fire.
79
+
80
+ Supports tool name matching, file path patterns, and custom conditions.
81
+
82
+ Example:
83
+ matcher = HookMatcher(
84
+ tool="Edit",
85
+ file_pattern=r"\\.(ts|tsx|js|jsx)$",
86
+ description="Match TypeScript/JavaScript file edits"
87
+ )
88
+ """
89
+
90
+ tool: str | None = Field(
91
+ default=None,
92
+ description="Tool name to match (e.g., 'Bash', 'Edit', 'Read')",
93
+ )
94
+ file_pattern: str | None = Field(
95
+ default=None,
96
+ description="Regex pattern to match file paths",
97
+ )
98
+ command_pattern: str | None = Field(
99
+ default=None,
100
+ description="Regex pattern to match command strings",
101
+ )
102
+ condition: str | None = Field(
103
+ default=None,
104
+ description="Custom condition expression",
105
+ )
106
+ match_all: bool = Field(
107
+ default=False,
108
+ description="If True, matches all events (wildcard)",
109
+ )
110
+ description: str = Field(
111
+ default="",
112
+ description="Human-readable description of match criteria",
113
+ )
114
+
115
+ def matches(self, context: dict[str, Any]) -> bool:
116
+ """Check if this matcher matches the given context.
117
+
118
+ Args:
119
+ context: Event context with keys like 'tool', 'file_path', 'command'
120
+
121
+ Returns:
122
+ True if matcher matches the context
123
+
124
+ """
125
+ import re
126
+
127
+ # Wildcard matches everything
128
+ if self.match_all:
129
+ return True
130
+
131
+ # Tool name matching
132
+ if self.tool and context.get("tool") != self.tool:
133
+ return False
134
+
135
+ # File path pattern matching
136
+ if self.file_pattern:
137
+ file_path = context.get("file_path", "")
138
+ if not re.search(self.file_pattern, file_path):
139
+ return False
140
+
141
+ # Command pattern matching
142
+ if self.command_pattern:
143
+ command = context.get("command", "")
144
+ if not re.search(self.command_pattern, command):
145
+ return False
146
+
147
+ return True
148
+
149
+
150
+ class HookRule(BaseModel):
151
+ """A complete hook rule with matcher and actions.
152
+
153
+ Example:
154
+ rule = HookRule(
155
+ matcher=HookMatcher(tool="Edit", file_pattern=r"\\.py$"),
156
+ hooks=[HookDefinition(type=HookType.PYTHON, command="format_python")],
157
+ description="Auto-format Python files after edits"
158
+ )
159
+ """
160
+
161
+ matcher: HookMatcher = Field(
162
+ default_factory=lambda: HookMatcher(match_all=True),
163
+ description="Conditions for when this rule fires",
164
+ )
165
+ hooks: list[HookDefinition] = Field(
166
+ default_factory=list,
167
+ description="List of hooks to execute when matched",
168
+ )
169
+ enabled: bool = Field(
170
+ default=True,
171
+ description="Whether this rule is active",
172
+ )
173
+ priority: int = Field(
174
+ default=0,
175
+ description="Execution priority (higher = earlier)",
176
+ )
177
+ description: str = Field(
178
+ default="",
179
+ description="Human-readable description",
180
+ )
181
+
182
+
183
+ class HookConfig(BaseModel):
184
+ """Complete hook configuration for an Empathy session.
185
+
186
+ Example YAML configuration:
187
+ hooks:
188
+ SessionStart:
189
+ - matcher:
190
+ match_all: true
191
+ hooks:
192
+ - type: python
193
+ command: attune_llm.hooks.scripts.session_start:main
194
+ description: Load previous context
195
+ PostToolUse:
196
+ - matcher:
197
+ tool: Edit
198
+ file_pattern: "\\.(py)$"
199
+ hooks:
200
+ - type: command
201
+ command: "ruff format {file_path}"
202
+ description: Auto-format Python files
203
+ """
204
+
205
+ hooks: dict[str, list[HookRule]] = Field(
206
+ default_factory=lambda: {event.value: [] for event in HookEvent},
207
+ description="Hooks organized by event type",
208
+ )
209
+ enabled: bool = Field(
210
+ default=True,
211
+ description="Global enable/disable for all hooks",
212
+ )
213
+ log_executions: bool = Field(
214
+ default=True,
215
+ description="Log hook executions for debugging",
216
+ )
217
+ default_timeout: int = Field(
218
+ default=30,
219
+ ge=1,
220
+ le=300,
221
+ description="Default timeout for hooks without explicit timeout",
222
+ )
223
+
224
+ def get_hooks_for_event(self, event: HookEvent) -> list[HookRule]:
225
+ """Get all hook rules for a specific event type.
226
+
227
+ Args:
228
+ event: The hook event type
229
+
230
+ Returns:
231
+ List of HookRule objects for this event, sorted by priority
232
+
233
+ """
234
+ rules = self.hooks.get(event.value, [])
235
+ return sorted(
236
+ [r for r in rules if r.enabled],
237
+ key=lambda r: -r.priority,
238
+ )
239
+
240
+ def add_hook(
241
+ self,
242
+ event: HookEvent,
243
+ hook: HookDefinition,
244
+ matcher: HookMatcher | None = None,
245
+ priority: int = 0,
246
+ ) -> None:
247
+ """Add a hook for an event.
248
+
249
+ Args:
250
+ event: Event type to hook
251
+ hook: Hook definition to add
252
+ matcher: Optional matcher (defaults to match_all)
253
+ priority: Execution priority
254
+
255
+ """
256
+ if event.value not in self.hooks:
257
+ self.hooks[event.value] = []
258
+
259
+ rule = HookRule(
260
+ matcher=matcher or HookMatcher(match_all=True),
261
+ hooks=[hook],
262
+ priority=priority,
263
+ )
264
+ self.hooks[event.value].append(rule)
265
+
266
+ @classmethod
267
+ def from_yaml(cls, yaml_path: str) -> "HookConfig":
268
+ """Load hook configuration from a YAML file.
269
+
270
+ Args:
271
+ yaml_path: Path to YAML configuration file
272
+
273
+ Returns:
274
+ HookConfig instance
275
+
276
+ """
277
+ from pathlib import Path
278
+
279
+ import yaml
280
+
281
+ config_file = Path(yaml_path)
282
+ if not config_file.exists():
283
+ return cls()
284
+
285
+ with open(config_file) as f:
286
+ data = yaml.safe_load(f) or {}
287
+
288
+ hooks_data = data.get("hooks", {})
289
+ return cls.model_validate({"hooks": hooks_data, **data})
290
+
291
+ def to_yaml(self, yaml_path: str) -> None:
292
+ """Save hook configuration to a YAML file.
293
+
294
+ Args:
295
+ yaml_path: Path to write YAML configuration
296
+
297
+ """
298
+ from pathlib import Path
299
+
300
+ import yaml
301
+
302
+ config_file = Path(yaml_path)
303
+ config_file.parent.mkdir(parents=True, exist_ok=True)
304
+
305
+ with open(config_file, "w") as f:
306
+ yaml.dump(self.model_dump(), f, default_flow_style=False)
@@ -0,0 +1,289 @@
1
+ """Hook Executor
2
+
3
+ Executes hook actions (commands, Python functions, webhooks).
4
+
5
+ Copyright 2025 Smart-AI-Memory
6
+ Licensed under Fair Source License 0.9
7
+ """
8
+
9
+ import asyncio
10
+ import importlib
11
+ import logging
12
+ import time
13
+ from collections.abc import Callable
14
+ from typing import Any
15
+
16
+ from attune_llm.hooks.config import HookDefinition, HookType
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class HookExecutor:
22
+ """Executor for running hook actions.
23
+
24
+ Supports three hook types:
25
+ - COMMAND: Run shell commands with variable substitution
26
+ - PYTHON: Import and call Python functions
27
+ - WEBHOOK: POST to webhook URLs
28
+
29
+ Example:
30
+ executor = HookExecutor()
31
+
32
+ # Execute a command hook
33
+ result = await executor.execute(
34
+ HookDefinition(type=HookType.COMMAND, command="echo {file_path}"),
35
+ context={"file_path": "/path/to/file.py"}
36
+ )
37
+ """
38
+
39
+ def __init__(self, python_handlers: dict[str, Callable] | None = None):
40
+ """Initialize the executor.
41
+
42
+ Args:
43
+ python_handlers: Map of handler IDs to Python callables
44
+
45
+ """
46
+ self._python_handlers = python_handlers or {}
47
+
48
+ async def execute(
49
+ self,
50
+ hook: HookDefinition,
51
+ context: dict[str, Any],
52
+ ) -> dict[str, Any]:
53
+ """Execute a hook action.
54
+
55
+ Args:
56
+ hook: Hook definition to execute
57
+ context: Execution context with variables
58
+
59
+ Returns:
60
+ Execution result dictionary
61
+
62
+ """
63
+ start_time = time.time()
64
+
65
+ try:
66
+ if hook.async_execution:
67
+ # Fire and forget
68
+ asyncio.create_task(self._execute_internal(hook, context))
69
+ return {
70
+ "success": True,
71
+ "output": "Hook scheduled for async execution",
72
+ "async": True,
73
+ "duration_ms": 0,
74
+ }
75
+
76
+ result = await asyncio.wait_for(
77
+ self._execute_internal(hook, context),
78
+ timeout=hook.timeout,
79
+ )
80
+
81
+ duration_ms = (time.time() - start_time) * 1000
82
+ return {
83
+ "success": True,
84
+ "output": result,
85
+ "duration_ms": round(duration_ms, 2),
86
+ }
87
+
88
+ except asyncio.TimeoutError:
89
+ duration_ms = (time.time() - start_time) * 1000
90
+ error_msg = f"Hook timed out after {hook.timeout}s"
91
+ logger.warning("%s: %s", error_msg, hook.command)
92
+ return {
93
+ "success": False,
94
+ "error": error_msg,
95
+ "duration_ms": round(duration_ms, 2),
96
+ }
97
+
98
+ except Exception as e:
99
+ duration_ms = (time.time() - start_time) * 1000
100
+ logger.error("Hook execution failed: %s - %s", hook.command, e)
101
+ return {
102
+ "success": False,
103
+ "error": str(e),
104
+ "duration_ms": round(duration_ms, 2),
105
+ }
106
+
107
+ async def _execute_internal(
108
+ self,
109
+ hook: HookDefinition,
110
+ context: dict[str, Any],
111
+ ) -> Any:
112
+ """Internal execution logic.
113
+
114
+ Args:
115
+ hook: Hook definition
116
+ context: Execution context
117
+
118
+ Returns:
119
+ Hook output
120
+
121
+ """
122
+ if hook.type == HookType.COMMAND:
123
+ return await self._execute_command(hook.command, context)
124
+ elif hook.type == HookType.PYTHON:
125
+ return await self._execute_python(hook.command, context)
126
+ elif hook.type == HookType.WEBHOOK:
127
+ return await self._execute_webhook(hook.command, context)
128
+ else:
129
+ raise ValueError(f"Unknown hook type: {hook.type}")
130
+
131
+ async def _execute_command(
132
+ self,
133
+ command: str,
134
+ context: dict[str, Any],
135
+ ) -> str:
136
+ """Execute a shell command.
137
+
138
+ Args:
139
+ command: Command string with optional {var} placeholders
140
+ context: Variables for substitution
141
+
142
+ Returns:
143
+ Command output
144
+
145
+ """
146
+ # Substitute context variables
147
+ try:
148
+ formatted_command = command.format(**context)
149
+ except KeyError as e:
150
+ raise ValueError(f"Missing context variable for command: {e}")
151
+
152
+ logger.debug("Executing command: %s", formatted_command)
153
+
154
+ # Run command asynchronously
155
+ process = await asyncio.create_subprocess_shell(
156
+ formatted_command,
157
+ stdout=asyncio.subprocess.PIPE,
158
+ stderr=asyncio.subprocess.PIPE,
159
+ )
160
+
161
+ stdout, stderr = await process.communicate()
162
+
163
+ if process.returncode != 0:
164
+ error_output = stderr.decode().strip() or stdout.decode().strip()
165
+ raise RuntimeError(
166
+ f"Command failed with exit code {process.returncode}: {error_output}"
167
+ )
168
+
169
+ return stdout.decode().strip()
170
+
171
+ async def _execute_python(
172
+ self,
173
+ command: str,
174
+ context: dict[str, Any],
175
+ ) -> Any:
176
+ """Execute a Python function.
177
+
178
+ Args:
179
+ command: Either a handler ID or module.path:function format
180
+ context: Context passed as kwargs to function
181
+
182
+ Returns:
183
+ Function return value
184
+
185
+ """
186
+ # Check if it's a registered handler ID
187
+ if command in self._python_handlers:
188
+ handler = self._python_handlers[command]
189
+ return await self._call_handler(handler, context)
190
+
191
+ # Otherwise, import module:function
192
+ if ":" not in command:
193
+ raise ValueError(f"Python hook must be 'module.path:function' format: {command}")
194
+
195
+ module_path, func_name = command.rsplit(":", 1)
196
+
197
+ try:
198
+ module = importlib.import_module(module_path)
199
+ handler = getattr(module, func_name)
200
+ except (ImportError, AttributeError) as e:
201
+ raise ValueError(f"Failed to import hook function: {command}") from e
202
+
203
+ return await self._call_handler(handler, context)
204
+
205
+ async def _call_handler(
206
+ self,
207
+ handler: Callable,
208
+ context: dict[str, Any],
209
+ ) -> Any:
210
+ """Call a handler function (sync or async).
211
+
212
+ Args:
213
+ handler: Callable to invoke
214
+ context: Context passed as kwargs
215
+
216
+ Returns:
217
+ Handler return value
218
+
219
+ """
220
+ if asyncio.iscoroutinefunction(handler):
221
+ return await handler(**context)
222
+ else:
223
+ # Run sync function in thread pool
224
+ loop = asyncio.get_event_loop()
225
+ return await loop.run_in_executor(None, lambda: handler(**context))
226
+
227
+ async def _execute_webhook(
228
+ self,
229
+ url: str,
230
+ context: dict[str, Any],
231
+ ) -> dict[str, Any]:
232
+ """Execute a webhook call.
233
+
234
+ Args:
235
+ url: Webhook URL
236
+ context: JSON payload to send
237
+
238
+ Returns:
239
+ Response data
240
+
241
+ """
242
+ try:
243
+ import aiohttp
244
+ except ImportError:
245
+ raise ImportError("aiohttp required for webhook hooks: pip install aiohttp")
246
+
247
+ logger.debug("Calling webhook: %s", url)
248
+
249
+ async with aiohttp.ClientSession() as session:
250
+ async with session.post(
251
+ url,
252
+ json=context,
253
+ headers={"Content-Type": "application/json"},
254
+ ) as response:
255
+ if response.status >= 400:
256
+ text = await response.text()
257
+ raise RuntimeError(f"Webhook failed with status {response.status}: {text}")
258
+
259
+ try:
260
+ return await response.json()
261
+ except Exception:
262
+ return {"status": response.status, "text": await response.text()}
263
+
264
+
265
+ class HookExecutorSync:
266
+ """Synchronous wrapper for HookExecutor.
267
+
268
+ For use in contexts where async is not available.
269
+ """
270
+
271
+ def __init__(self, python_handlers: dict[str, Callable] | None = None):
272
+ self._executor = HookExecutor(python_handlers)
273
+
274
+ def execute(
275
+ self,
276
+ hook: HookDefinition,
277
+ context: dict[str, Any],
278
+ ) -> dict[str, Any]:
279
+ """Execute a hook synchronously.
280
+
281
+ Args:
282
+ hook: Hook definition
283
+ context: Execution context
284
+
285
+ Returns:
286
+ Execution result
287
+
288
+ """
289
+ return asyncio.run(self._executor.execute(hook, context))