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,560 @@
1
+ """Learned Skills Storage for Continuous Learning
2
+
3
+ Persists and retrieves learned patterns and skills.
4
+ Provides storage for patterns extracted from sessions.
5
+
6
+ Architectural patterns inspired by everything-claude-code by Affaan Mustafa.
7
+ See: https://github.com/affaan-m/everything-claude-code (MIT License)
8
+ See: ACKNOWLEDGMENTS.md for full attribution.
9
+
10
+ Copyright 2025 Smart AI Memory, LLC
11
+ Licensed under Fair Source 0.9
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import json
17
+ import logging
18
+ from dataclasses import dataclass, field
19
+ from datetime import datetime
20
+ from pathlib import Path
21
+ from typing import Any
22
+
23
+ from attune_llm.learning.extractor import ExtractedPattern, PatternCategory
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ @dataclass
29
+ class LearnedSkill:
30
+ """A skill learned from pattern aggregation.
31
+
32
+ Skills are higher-level learnings derived from
33
+ multiple related patterns.
34
+ """
35
+
36
+ skill_id: str
37
+ name: str
38
+ description: str
39
+ category: PatternCategory
40
+ patterns: list[str] # Pattern IDs
41
+ confidence: float
42
+ usage_count: int = 0
43
+ last_used: datetime | None = None
44
+ created_at: datetime = field(default_factory=datetime.now)
45
+ tags: list[str] = field(default_factory=list)
46
+ metadata: dict[str, Any] = field(default_factory=dict)
47
+
48
+ def to_dict(self) -> dict[str, Any]:
49
+ """Convert to dictionary."""
50
+ return {
51
+ "skill_id": self.skill_id,
52
+ "name": self.name,
53
+ "description": self.description,
54
+ "category": self.category.value,
55
+ "patterns": self.patterns,
56
+ "confidence": self.confidence,
57
+ "usage_count": self.usage_count,
58
+ "last_used": self.last_used.isoformat() if self.last_used else None,
59
+ "created_at": self.created_at.isoformat(),
60
+ "tags": self.tags,
61
+ "metadata": self.metadata,
62
+ }
63
+
64
+ @classmethod
65
+ def from_dict(cls, data: dict[str, Any]) -> LearnedSkill:
66
+ """Create from dictionary."""
67
+ return cls(
68
+ skill_id=data["skill_id"],
69
+ name=data["name"],
70
+ description=data["description"],
71
+ category=PatternCategory(data["category"]),
72
+ patterns=data.get("patterns", []),
73
+ confidence=data.get("confidence", 0.5),
74
+ usage_count=data.get("usage_count", 0),
75
+ last_used=datetime.fromisoformat(data["last_used"]) if data.get("last_used") else None,
76
+ created_at=datetime.fromisoformat(data["created_at"])
77
+ if "created_at" in data
78
+ else datetime.now(),
79
+ tags=data.get("tags", []),
80
+ metadata=data.get("metadata", {}),
81
+ )
82
+
83
+
84
+ class LearnedSkillsStorage:
85
+ """Manages storage of learned patterns and skills.
86
+
87
+ Provides persistence and retrieval for patterns extracted
88
+ from collaboration sessions, with support for querying
89
+ and filtering.
90
+ """
91
+
92
+ def __init__(
93
+ self,
94
+ storage_dir: str | Path = ".attune/learned_skills",
95
+ max_patterns_per_user: int = 100,
96
+ max_skills_per_user: int = 50,
97
+ ):
98
+ """Initialize the storage.
99
+
100
+ Args:
101
+ storage_dir: Directory for storage files
102
+ max_patterns_per_user: Maximum patterns to store per user
103
+ max_skills_per_user: Maximum skills to store per user
104
+ """
105
+ self.storage_dir = Path(storage_dir)
106
+ self._max_patterns = max_patterns_per_user
107
+ self._max_skills = max_skills_per_user
108
+
109
+ def _ensure_storage(self) -> None:
110
+ """Ensure storage directory exists."""
111
+ self.storage_dir.mkdir(parents=True, exist_ok=True)
112
+
113
+ def _get_user_dir(self, user_id: str) -> Path:
114
+ """Get storage directory for a user."""
115
+ safe_id = "".join(c if c.isalnum() or c in "-_" else "_" for c in user_id)
116
+ return self.storage_dir / safe_id
117
+
118
+ def _get_patterns_file(self, user_id: str) -> Path:
119
+ """Get patterns file path for a user."""
120
+ return self._get_user_dir(user_id) / "patterns.json"
121
+
122
+ def _get_skills_file(self, user_id: str) -> Path:
123
+ """Get skills file path for a user."""
124
+ return self._get_user_dir(user_id) / "skills.json"
125
+
126
+ # Pattern operations
127
+
128
+ def save_pattern(
129
+ self,
130
+ user_id: str,
131
+ pattern: ExtractedPattern,
132
+ ) -> str:
133
+ """Save a pattern for a user.
134
+
135
+ Args:
136
+ user_id: User identifier
137
+ pattern: Pattern to save
138
+
139
+ Returns:
140
+ Pattern ID
141
+ """
142
+ self._ensure_storage()
143
+ user_dir = self._get_user_dir(user_id)
144
+ user_dir.mkdir(parents=True, exist_ok=True)
145
+
146
+ patterns = self._load_patterns(user_id)
147
+
148
+ # Check for duplicate
149
+ existing_ids = {p["pattern_id"] for p in patterns}
150
+ if pattern.pattern_id in existing_ids:
151
+ logger.debug(f"Pattern {pattern.pattern_id} already exists, updating")
152
+ patterns = [p for p in patterns if p["pattern_id"] != pattern.pattern_id]
153
+
154
+ patterns.append(pattern.to_dict())
155
+
156
+ # Enforce limit (remove oldest)
157
+ if len(patterns) > self._max_patterns:
158
+ patterns = sorted(
159
+ patterns,
160
+ key=lambda p: p.get("extracted_at", ""),
161
+ reverse=True,
162
+ )[: self._max_patterns]
163
+
164
+ self._save_patterns(user_id, patterns)
165
+ logger.info(f"Saved pattern {pattern.pattern_id} for user {user_id}")
166
+
167
+ return pattern.pattern_id
168
+
169
+ def save_patterns(
170
+ self,
171
+ user_id: str,
172
+ patterns: list[ExtractedPattern],
173
+ ) -> list[str]:
174
+ """Save multiple patterns.
175
+
176
+ Args:
177
+ user_id: User identifier
178
+ patterns: Patterns to save
179
+
180
+ Returns:
181
+ List of saved pattern IDs
182
+ """
183
+ return [self.save_pattern(user_id, p) for p in patterns]
184
+
185
+ def get_pattern(
186
+ self,
187
+ user_id: str,
188
+ pattern_id: str,
189
+ ) -> ExtractedPattern | None:
190
+ """Get a specific pattern.
191
+
192
+ Args:
193
+ user_id: User identifier
194
+ pattern_id: Pattern identifier
195
+
196
+ Returns:
197
+ Pattern or None if not found
198
+ """
199
+ patterns = self._load_patterns(user_id)
200
+
201
+ for p in patterns:
202
+ if p.get("pattern_id") == pattern_id:
203
+ return ExtractedPattern.from_dict(p)
204
+
205
+ return None
206
+
207
+ def get_all_patterns(self, user_id: str) -> list[ExtractedPattern]:
208
+ """Get all patterns for a user.
209
+
210
+ Args:
211
+ user_id: User identifier
212
+
213
+ Returns:
214
+ List of patterns
215
+ """
216
+ patterns = self._load_patterns(user_id)
217
+ return [ExtractedPattern.from_dict(p) for p in patterns]
218
+
219
+ def get_patterns_by_category(
220
+ self,
221
+ user_id: str,
222
+ category: PatternCategory,
223
+ ) -> list[ExtractedPattern]:
224
+ """Get patterns by category.
225
+
226
+ Args:
227
+ user_id: User identifier
228
+ category: Pattern category
229
+
230
+ Returns:
231
+ List of matching patterns
232
+ """
233
+ all_patterns = self.get_all_patterns(user_id)
234
+ return [p for p in all_patterns if p.category == category]
235
+
236
+ def get_patterns_by_tag(
237
+ self,
238
+ user_id: str,
239
+ tag: str,
240
+ ) -> list[ExtractedPattern]:
241
+ """Get patterns by tag.
242
+
243
+ Args:
244
+ user_id: User identifier
245
+ tag: Tag to filter by
246
+
247
+ Returns:
248
+ List of matching patterns
249
+ """
250
+ all_patterns = self.get_all_patterns(user_id)
251
+ return [p for p in all_patterns if tag in p.tags]
252
+
253
+ def search_patterns(
254
+ self,
255
+ user_id: str,
256
+ query: str,
257
+ ) -> list[ExtractedPattern]:
258
+ """Search patterns by trigger or context.
259
+
260
+ Args:
261
+ user_id: User identifier
262
+ query: Search query
263
+
264
+ Returns:
265
+ List of matching patterns
266
+ """
267
+ all_patterns = self.get_all_patterns(user_id)
268
+ query_lower = query.lower()
269
+
270
+ return [
271
+ p
272
+ for p in all_patterns
273
+ if query_lower in p.trigger.lower()
274
+ or query_lower in p.context.lower()
275
+ or query_lower in p.resolution.lower()
276
+ ]
277
+
278
+ def delete_pattern(self, user_id: str, pattern_id: str) -> bool:
279
+ """Delete a pattern.
280
+
281
+ Args:
282
+ user_id: User identifier
283
+ pattern_id: Pattern to delete
284
+
285
+ Returns:
286
+ True if deleted
287
+ """
288
+ patterns = self._load_patterns(user_id)
289
+ original_count = len(patterns)
290
+
291
+ patterns = [p for p in patterns if p.get("pattern_id") != pattern_id]
292
+
293
+ if len(patterns) < original_count:
294
+ self._save_patterns(user_id, patterns)
295
+ return True
296
+
297
+ return False
298
+
299
+ def _load_patterns(self, user_id: str) -> list[dict[str, Any]]:
300
+ """Load patterns from storage."""
301
+ patterns_file = self._get_patterns_file(user_id)
302
+
303
+ if not patterns_file.exists():
304
+ return []
305
+
306
+ try:
307
+ with open(patterns_file, encoding="utf-8") as f:
308
+ return json.load(f)
309
+ except (json.JSONDecodeError, OSError) as e:
310
+ logger.error(f"Failed to load patterns: {e}")
311
+ return []
312
+
313
+ def _save_patterns(
314
+ self,
315
+ user_id: str,
316
+ patterns: list[dict[str, Any]],
317
+ ) -> None:
318
+ """Save patterns to storage."""
319
+ patterns_file = self._get_patterns_file(user_id)
320
+
321
+ with open(patterns_file, "w", encoding="utf-8") as f:
322
+ json.dump(patterns, f, indent=2, default=str)
323
+
324
+ # Skill operations
325
+
326
+ def save_skill(
327
+ self,
328
+ user_id: str,
329
+ skill: LearnedSkill,
330
+ ) -> str:
331
+ """Save a learned skill.
332
+
333
+ Args:
334
+ user_id: User identifier
335
+ skill: Skill to save
336
+
337
+ Returns:
338
+ Skill ID
339
+ """
340
+ self._ensure_storage()
341
+ user_dir = self._get_user_dir(user_id)
342
+ user_dir.mkdir(parents=True, exist_ok=True)
343
+
344
+ skills = self._load_skills(user_id)
345
+
346
+ # Check for duplicate
347
+ existing_ids = {s["skill_id"] for s in skills}
348
+ if skill.skill_id in existing_ids:
349
+ skills = [s for s in skills if s["skill_id"] != skill.skill_id]
350
+
351
+ skills.append(skill.to_dict())
352
+
353
+ # Enforce limit
354
+ if len(skills) > self._max_skills:
355
+ skills = sorted(
356
+ skills,
357
+ key=lambda s: s.get("created_at", ""),
358
+ reverse=True,
359
+ )[: self._max_skills]
360
+
361
+ self._save_skills(user_id, skills)
362
+ return skill.skill_id
363
+
364
+ def get_skill(
365
+ self,
366
+ user_id: str,
367
+ skill_id: str,
368
+ ) -> LearnedSkill | None:
369
+ """Get a specific skill.
370
+
371
+ Args:
372
+ user_id: User identifier
373
+ skill_id: Skill identifier
374
+
375
+ Returns:
376
+ Skill or None
377
+ """
378
+ skills = self._load_skills(user_id)
379
+
380
+ for s in skills:
381
+ if s.get("skill_id") == skill_id:
382
+ return LearnedSkill.from_dict(s)
383
+
384
+ return None
385
+
386
+ def get_all_skills(self, user_id: str) -> list[LearnedSkill]:
387
+ """Get all skills for a user.
388
+
389
+ Args:
390
+ user_id: User identifier
391
+
392
+ Returns:
393
+ List of skills
394
+ """
395
+ skills = self._load_skills(user_id)
396
+ return [LearnedSkill.from_dict(s) for s in skills]
397
+
398
+ def record_skill_usage(
399
+ self,
400
+ user_id: str,
401
+ skill_id: str,
402
+ ) -> None:
403
+ """Record that a skill was used.
404
+
405
+ Args:
406
+ user_id: User identifier
407
+ skill_id: Skill that was used
408
+ """
409
+ skills = self._load_skills(user_id)
410
+
411
+ for s in skills:
412
+ if s.get("skill_id") == skill_id:
413
+ s["usage_count"] = s.get("usage_count", 0) + 1
414
+ s["last_used"] = datetime.now().isoformat()
415
+ break
416
+
417
+ self._save_skills(user_id, skills)
418
+
419
+ def delete_skill(self, user_id: str, skill_id: str) -> bool:
420
+ """Delete a skill.
421
+
422
+ Args:
423
+ user_id: User identifier
424
+ skill_id: Skill to delete
425
+
426
+ Returns:
427
+ True if deleted
428
+ """
429
+ skills = self._load_skills(user_id)
430
+ original_count = len(skills)
431
+
432
+ skills = [s for s in skills if s.get("skill_id") != skill_id]
433
+
434
+ if len(skills) < original_count:
435
+ self._save_skills(user_id, skills)
436
+ return True
437
+
438
+ return False
439
+
440
+ def _load_skills(self, user_id: str) -> list[dict[str, Any]]:
441
+ """Load skills from storage."""
442
+ skills_file = self._get_skills_file(user_id)
443
+
444
+ if not skills_file.exists():
445
+ return []
446
+
447
+ try:
448
+ with open(skills_file, encoding="utf-8") as f:
449
+ return json.load(f)
450
+ except (json.JSONDecodeError, OSError) as e:
451
+ logger.error(f"Failed to load skills: {e}")
452
+ return []
453
+
454
+ def _save_skills(
455
+ self,
456
+ user_id: str,
457
+ skills: list[dict[str, Any]],
458
+ ) -> None:
459
+ """Save skills to storage."""
460
+ skills_file = self._get_skills_file(user_id)
461
+
462
+ with open(skills_file, "w", encoding="utf-8") as f:
463
+ json.dump(skills, f, indent=2, default=str)
464
+
465
+ # Summary operations
466
+
467
+ def get_summary(self, user_id: str) -> dict[str, Any]:
468
+ """Get learning summary for a user.
469
+
470
+ Args:
471
+ user_id: User identifier
472
+
473
+ Returns:
474
+ Summary dictionary
475
+ """
476
+ patterns = self.get_all_patterns(user_id)
477
+ skills = self.get_all_skills(user_id)
478
+
479
+ # Count by category
480
+ category_counts: dict[str, int] = {}
481
+ for pattern in patterns:
482
+ cat = pattern.category.value
483
+ category_counts[cat] = category_counts.get(cat, 0) + 1
484
+
485
+ return {
486
+ "user_id": user_id,
487
+ "total_patterns": len(patterns),
488
+ "total_skills": len(skills),
489
+ "patterns_by_category": category_counts,
490
+ "avg_confidence": (
491
+ sum(p.confidence for p in patterns) / len(patterns) if patterns else 0.0
492
+ ),
493
+ "most_used_skill": (max(skills, key=lambda s: s.usage_count).name if skills else None),
494
+ }
495
+
496
+ def clear_user_data(self, user_id: str) -> int:
497
+ """Clear all data for a user.
498
+
499
+ Args:
500
+ user_id: User identifier
501
+
502
+ Returns:
503
+ Number of items cleared
504
+ """
505
+ user_dir = self._get_user_dir(user_id)
506
+ count = 0
507
+
508
+ if user_dir.exists():
509
+ for file in user_dir.glob("*.json"):
510
+ try:
511
+ # Count items before deleting
512
+ with open(file, encoding="utf-8") as f:
513
+ data = json.load(f)
514
+ count += len(data) if isinstance(data, list) else 1
515
+ file.unlink()
516
+ except (OSError, json.JSONDecodeError):
517
+ continue
518
+
519
+ try:
520
+ user_dir.rmdir()
521
+ except OSError:
522
+ pass
523
+
524
+ return count
525
+
526
+ def format_patterns_for_context(
527
+ self,
528
+ user_id: str,
529
+ max_patterns: int = 5,
530
+ categories: list[PatternCategory] | None = None,
531
+ ) -> str:
532
+ """Format patterns for injection into context.
533
+
534
+ Args:
535
+ user_id: User identifier
536
+ max_patterns: Maximum patterns to include
537
+ categories: Optional category filter
538
+
539
+ Returns:
540
+ Formatted markdown string
541
+ """
542
+ patterns = self.get_all_patterns(user_id)
543
+
544
+ if categories:
545
+ patterns = [p for p in patterns if p.category in categories]
546
+
547
+ # Sort by confidence
548
+ patterns = sorted(patterns, key=lambda p: p.confidence, reverse=True)
549
+ patterns = patterns[:max_patterns]
550
+
551
+ if not patterns:
552
+ return ""
553
+
554
+ lines = ["## Learned Patterns", ""]
555
+
556
+ for pattern in patterns:
557
+ lines.append(pattern.format_readable())
558
+ lines.append("")
559
+
560
+ return "\n".join(lines)