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,509 @@
1
+ """Keyboard Shortcuts Workflow
2
+
3
+ Generates optimized keyboard shortcuts for any project following
4
+ the "Keyboard Conductor" musical scale pattern.
5
+
6
+ Stages:
7
+ 1. DISCOVER - Parse features from project sources
8
+ 2. ANALYZE - Categorize features and suggest mnemonics (LLM)
9
+ 3. GENERATE - Create shortcuts for all layouts (LLM)
10
+ 4. VALIDATE - Check for conflicts and ergonomic issues (LLM)
11
+ 5. EXPORT - Generate output files (local)
12
+ """
13
+
14
+ import json
15
+ from pathlib import Path
16
+ from typing import Any
17
+
18
+ import yaml
19
+
20
+ from attune.workflows.base import BaseWorkflow, ModelTier
21
+
22
+ from .generators import ComprehensiveGenerator
23
+ from .parsers import CompositeParser
24
+ from .prompts import format_analyze_prompt, format_generate_prompt, format_validate_prompt
25
+ from .schema import (
26
+ FeatureManifest,
27
+ FrequencyTier,
28
+ GeneratedShortcuts,
29
+ KeyboardLayout,
30
+ LayoutShortcuts,
31
+ ScaleAssignments,
32
+ ShortcutAssignment,
33
+ )
34
+
35
+
36
+ class KeyboardShortcutWorkflow(BaseWorkflow):
37
+ """Generate optimized keyboard shortcuts for any project.
38
+
39
+ Uses the "Keyboard Conductor" pattern:
40
+ - Scale 1 (Daily): 4 most-used features on home row
41
+ - Scale 2 (Frequent): Next 4 features on adjacent keys
42
+ - Scale 3 (Advanced): Remaining features logically placed
43
+ """
44
+
45
+ name = "keyboard-shortcuts"
46
+ description = "Generate ergonomic keyboard shortcuts with multi-layout support"
47
+ stages = ["discover", "analyze", "generate", "validate", "export"]
48
+ tier_map = {
49
+ "discover": ModelTier.CHEAP, # Parse files, no LLM
50
+ "analyze": ModelTier.CAPABLE, # Categorize features
51
+ "generate": ModelTier.CAPABLE, # Generate shortcuts
52
+ "validate": ModelTier.CHEAP, # Check conflicts
53
+ "export": ModelTier.CHEAP, # Write files, no LLM
54
+ }
55
+
56
+ def __init__(self, **kwargs):
57
+ super().__init__(**kwargs)
58
+ self.parser = CompositeParser()
59
+ self.generator = ComprehensiveGenerator()
60
+
61
+ async def run_stage(
62
+ self,
63
+ stage_name: str,
64
+ tier: ModelTier,
65
+ input_data: dict[str, Any],
66
+ ) -> tuple[Any, int, int]:
67
+ """Execute a single workflow stage."""
68
+ if stage_name == "discover":
69
+ return await self._discover_features(input_data)
70
+ if stage_name == "analyze":
71
+ return await self._analyze_features(input_data, tier)
72
+ if stage_name == "generate":
73
+ return await self._generate_shortcuts(input_data, tier)
74
+ if stage_name == "validate":
75
+ return await self._validate_shortcuts(input_data, tier)
76
+ if stage_name == "export":
77
+ return await self._export_outputs(input_data)
78
+ raise ValueError(f"Unknown stage: {stage_name}")
79
+
80
+ def should_skip_stage(
81
+ self,
82
+ stage_name: str,
83
+ input_data: dict[str, Any],
84
+ ) -> tuple[bool, str | None]:
85
+ """Check if a stage should be skipped."""
86
+ if stage_name == "analyze":
87
+ # Skip LLM analysis if features already have frequencies set
88
+ manifest = input_data.get("manifest")
89
+ if manifest and all(f.frequency for f in manifest.all_features()):
90
+ return True, "Features already categorized"
91
+
92
+ if stage_name == "validate":
93
+ # Skip validation if user explicitly requests it
94
+ if input_data.get("skip_validation", False):
95
+ return True, "Validation skipped by user request"
96
+
97
+ return False, None
98
+
99
+ # ========================================================================
100
+ # Stage 1: Discover Features
101
+ # ========================================================================
102
+
103
+ async def _discover_features(
104
+ self,
105
+ input_data: dict[str, Any],
106
+ ) -> tuple[dict[str, Any], int, int]:
107
+ """Parse features from project sources.
108
+
109
+ Supports:
110
+ - VSCode package.json commands
111
+ - Python pyproject.toml entry points
112
+ - Custom features.yaml manifest
113
+ """
114
+ project_path = Path(input_data.get("path", "."))
115
+
116
+ # Use the composite parser to discover features
117
+ manifest = self.parser.discover_features(project_path)
118
+
119
+ # If no features found, return early
120
+ if not manifest.all_features():
121
+ return (
122
+ {
123
+ "manifest": manifest,
124
+ "feature_count": 0,
125
+ "error": "No features found in project",
126
+ },
127
+ 0,
128
+ 0,
129
+ )
130
+
131
+ return (
132
+ {
133
+ "manifest": manifest,
134
+ "feature_count": len(manifest.all_features()),
135
+ "categories": len(manifest.categories),
136
+ },
137
+ 0,
138
+ 0,
139
+ )
140
+
141
+ # ========================================================================
142
+ # Stage 2: Analyze Features (LLM)
143
+ # ========================================================================
144
+
145
+ async def _analyze_features(
146
+ self,
147
+ input_data: dict[str, Any],
148
+ tier: ModelTier,
149
+ ) -> tuple[dict[str, Any], int, int]:
150
+ """Use LLM to categorize features and suggest mnemonics.
151
+
152
+ Input: FeatureManifest with discovered features
153
+ Output: Enhanced manifest with frequency tiers and mnemonic suggestions
154
+ """
155
+ manifest: FeatureManifest = input_data["manifest"]
156
+
157
+ # Convert features to YAML for the prompt
158
+ features_yaml = self._features_to_yaml(manifest)
159
+
160
+ # Format the analysis prompt
161
+ prompt = format_analyze_prompt(
162
+ project_name=manifest.project_name,
163
+ project_type=manifest.project_type,
164
+ feature_count=len(manifest.all_features()),
165
+ features_yaml=features_yaml,
166
+ )
167
+
168
+ # Call LLM (using inherited method from BaseWorkflow)
169
+ response, in_tokens, out_tokens = await self._invoke_llm(
170
+ prompt=prompt,
171
+ tier=tier,
172
+ system="You are a UX expert specializing in keyboard ergonomics.",
173
+ )
174
+
175
+ # Parse the response
176
+ analysis = self._parse_yaml_response(response)
177
+
178
+ # Update manifest with analysis results
179
+ if analysis and "analyzed_features" in analysis:
180
+ self._update_manifest_from_analysis(manifest, analysis)
181
+
182
+ return (
183
+ {
184
+ "manifest": manifest,
185
+ "analysis": analysis,
186
+ "phrase_mnemonic": analysis.get("phrase_mnemonic", "") if analysis else "",
187
+ },
188
+ in_tokens,
189
+ out_tokens,
190
+ )
191
+
192
+ # ========================================================================
193
+ # Stage 3: Generate Shortcuts (LLM)
194
+ # ========================================================================
195
+
196
+ async def _generate_shortcuts(
197
+ self,
198
+ input_data: dict[str, Any],
199
+ tier: ModelTier,
200
+ ) -> tuple[dict[str, Any], int, int]:
201
+ """Use LLM to generate optimal shortcuts for each layout.
202
+
203
+ Input: Analyzed manifest
204
+ Output: GeneratedShortcuts with shortcuts for all layouts
205
+ """
206
+ manifest: FeatureManifest = input_data["manifest"]
207
+ analysis = input_data.get("analysis", {})
208
+
209
+ # Format the generation prompt
210
+ prompt = format_generate_prompt(
211
+ analyzed_yaml=yaml.dump(analysis) if analysis else self._features_to_yaml(manifest),
212
+ existing_shortcuts="[]",
213
+ reserved_keys='["q", "w", "s", "z", "x"]',
214
+ )
215
+
216
+ # Call LLM
217
+ response, in_tokens, out_tokens = await self._invoke_llm(
218
+ prompt=prompt,
219
+ tier=tier,
220
+ system="You are a keyboard layout specialist.",
221
+ )
222
+
223
+ # Parse the response
224
+ shortcuts_data = self._parse_json_response(response)
225
+
226
+ # Build GeneratedShortcuts from response
227
+ generated = self._build_generated_shortcuts(manifest, shortcuts_data)
228
+
229
+ return (
230
+ {
231
+ "manifest": manifest,
232
+ "generated": generated,
233
+ },
234
+ in_tokens,
235
+ out_tokens,
236
+ )
237
+
238
+ # ========================================================================
239
+ # Stage 4: Validate Shortcuts (LLM)
240
+ # ========================================================================
241
+
242
+ async def _validate_shortcuts(
243
+ self,
244
+ input_data: dict[str, Any],
245
+ tier: ModelTier,
246
+ ) -> tuple[dict[str, Any], int, int]:
247
+ """Validate generated shortcuts for conflicts and issues.
248
+
249
+ Input: GeneratedShortcuts
250
+ Output: Validated shortcuts with any warnings/conflicts
251
+ """
252
+ generated: GeneratedShortcuts = input_data["generated"]
253
+
254
+ # Format the validation prompt
255
+ shortcuts_json = json.dumps(
256
+ {
257
+ layout.value: {
258
+ "shortcuts": [
259
+ {"key": s.key, "feature_id": s.feature_id, "mnemonic": s.mnemonic}
260
+ for s in layout_shortcuts.shortcuts
261
+ ],
262
+ }
263
+ for layout, layout_shortcuts in generated.layouts.items()
264
+ },
265
+ indent=2,
266
+ )
267
+
268
+ prompt = format_validate_prompt(shortcuts_json)
269
+
270
+ # Call LLM
271
+ response, in_tokens, out_tokens = await self._invoke_llm(
272
+ prompt=prompt,
273
+ tier=tier,
274
+ system="You are a keyboard shortcut validator.",
275
+ )
276
+
277
+ # Parse the response
278
+ validation = self._parse_json_response(response)
279
+
280
+ # Update generated shortcuts with validation results
281
+ if validation:
282
+ generated.validation_passed = validation.get("valid", True)
283
+ generated.conflicts = validation.get("conflicts", [])
284
+ generated.warnings = validation.get("warnings", [])
285
+
286
+ return (
287
+ {
288
+ "manifest": input_data["manifest"],
289
+ "generated": generated,
290
+ "validation": validation,
291
+ },
292
+ in_tokens,
293
+ out_tokens,
294
+ )
295
+
296
+ # ========================================================================
297
+ # Stage 5: Export Outputs
298
+ # ========================================================================
299
+
300
+ async def _export_outputs(
301
+ self,
302
+ input_data: dict[str, Any],
303
+ ) -> tuple[dict[str, Any], int, int]:
304
+ """Generate output files in all formats.
305
+
306
+ Output:
307
+ - VSCode keybindings (per layout)
308
+ - CLI aliases script
309
+ - Markdown documentation
310
+ """
311
+ generated: GeneratedShortcuts = input_data["generated"]
312
+ output_dir = Path(input_data.get("output_dir", "."))
313
+
314
+ # Generate all outputs
315
+ generated_files = self.generator.generate_all(generated, output_dir)
316
+
317
+ return (
318
+ {
319
+ "generated": generated,
320
+ "output_files": generated_files,
321
+ "output_dir": str(output_dir),
322
+ },
323
+ 0,
324
+ 0,
325
+ )
326
+
327
+ # ========================================================================
328
+ # Helper Methods
329
+ # ========================================================================
330
+
331
+ def _features_to_yaml(self, manifest: FeatureManifest) -> str:
332
+ """Convert features to YAML for LLM prompts."""
333
+ features_list = []
334
+ for feature in manifest.all_features():
335
+ features_list.append(
336
+ {
337
+ "id": feature.id,
338
+ "name": feature.name,
339
+ "description": feature.description,
340
+ "frequency": feature.frequency.value,
341
+ "context": feature.context,
342
+ },
343
+ )
344
+ return yaml.dump({"features": features_list}, default_flow_style=False)
345
+
346
+ def _parse_yaml_response(self, response: str) -> dict[str, Any] | None:
347
+ """Parse YAML from LLM response."""
348
+ try:
349
+ # Try to extract YAML from code blocks
350
+ if "```yaml" in response:
351
+ yaml_content = response.split("```yaml")[1].split("```")[0]
352
+ elif "```" in response:
353
+ yaml_content = response.split("```")[1].split("```")[0]
354
+ else:
355
+ yaml_content = response
356
+
357
+ result = yaml.safe_load(yaml_content.strip())
358
+ if isinstance(result, dict):
359
+ return result
360
+ return None
361
+ except Exception: # noqa: BLE001
362
+ # INTENTIONAL: LLM responses may have unparseable YAML.
363
+ # Return None and let caller handle fallback gracefully.
364
+ return None
365
+
366
+ def _parse_json_response(self, response: str) -> dict[str, Any] | None:
367
+ """Parse JSON from LLM response."""
368
+ try:
369
+ # Try to extract JSON from code blocks
370
+ if "```json" in response:
371
+ json_content = response.split("```json")[1].split("```")[0]
372
+ elif "```" in response:
373
+ json_content = response.split("```")[1].split("```")[0]
374
+ else:
375
+ json_content = response
376
+
377
+ result = json.loads(json_content.strip())
378
+ if isinstance(result, dict):
379
+ return result
380
+ return None
381
+ except Exception: # noqa: BLE001
382
+ # INTENTIONAL: LLM responses may have unparseable JSON.
383
+ # Return None and let caller handle fallback gracefully.
384
+ return None
385
+
386
+ def _update_manifest_from_analysis(
387
+ self,
388
+ manifest: FeatureManifest,
389
+ analysis: dict,
390
+ ) -> None:
391
+ """Update manifest features with analysis results."""
392
+ analyzed = {f["id"]: f for f in analysis.get("analyzed_features", [])}
393
+
394
+ for feature in manifest.all_features():
395
+ if feature.id in analyzed:
396
+ af = analyzed[feature.id]
397
+ freq_str = af.get("frequency", "frequent")
398
+ if freq_str in FrequencyTier._value2member_map_:
399
+ feature.frequency = FrequencyTier(freq_str)
400
+
401
+ def _build_generated_shortcuts(
402
+ self,
403
+ manifest: FeatureManifest,
404
+ shortcuts_data: dict | None,
405
+ ) -> GeneratedShortcuts:
406
+ """Build GeneratedShortcuts from LLM response."""
407
+ generated = GeneratedShortcuts(manifest=manifest)
408
+
409
+ if not shortcuts_data:
410
+ # Fallback: generate basic shortcuts
411
+ generated = self._generate_fallback_shortcuts(manifest)
412
+ return generated
413
+
414
+ # Process each layout
415
+ for layout_str in ["qwerty", "dvorak", "colemak"]:
416
+ if layout_str not in shortcuts_data:
417
+ continue
418
+
419
+ layout = KeyboardLayout(layout_str)
420
+ layout_data = shortcuts_data[layout_str]
421
+
422
+ shortcuts = []
423
+ for s in layout_data.get("shortcuts", []):
424
+ shortcuts.append(
425
+ ShortcutAssignment(
426
+ feature_id=s["feature_id"],
427
+ key=s["key"],
428
+ mnemonic=s.get("mnemonic", f"{s['key'].upper()} = {s['feature_id']}"),
429
+ layout=layout,
430
+ ),
431
+ )
432
+
433
+ scales = layout_data.get("scale_assignments", {})
434
+ scale_assignments = ScaleAssignments(
435
+ daily=scales.get("daily", []),
436
+ frequent=scales.get("frequent", []),
437
+ advanced=scales.get("advanced", []),
438
+ )
439
+
440
+ generated.layouts[layout] = LayoutShortcuts(
441
+ layout=layout,
442
+ shortcuts=shortcuts,
443
+ scale_assignments=scale_assignments,
444
+ phrase_mnemonic=layout_data.get("phrase_mnemonic", ""),
445
+ )
446
+
447
+ return generated
448
+
449
+ def _generate_fallback_shortcuts(self, manifest: FeatureManifest) -> GeneratedShortcuts:
450
+ """Generate basic shortcuts without LLM."""
451
+ generated = GeneratedShortcuts(manifest=manifest)
452
+ features = manifest.all_features()
453
+
454
+ # Simple assignment: use first letter of feature ID
455
+ used_keys: set[str] = set()
456
+ shortcuts = []
457
+
458
+ for feature in features:
459
+ # Try first letter
460
+ key = feature.id[0].lower()
461
+ if key in used_keys:
462
+ # Find next available letter
463
+ for c in feature.id.lower():
464
+ if c.isalpha() and c not in used_keys:
465
+ key = c
466
+ break
467
+ else:
468
+ # Use any available letter
469
+ for c in "abcdefghijklmnopqrstuvwxyz":
470
+ if c not in used_keys:
471
+ key = c
472
+ break
473
+
474
+ used_keys.add(key)
475
+ shortcuts.append(
476
+ ShortcutAssignment(
477
+ feature_id=feature.id,
478
+ key=key,
479
+ mnemonic=f"{key.upper()} = {feature.name}",
480
+ ),
481
+ )
482
+
483
+ # Assign to QWERTY layout
484
+ generated.layouts[KeyboardLayout.QWERTY] = LayoutShortcuts(
485
+ layout=KeyboardLayout.QWERTY,
486
+ shortcuts=shortcuts,
487
+ phrase_mnemonic="First letter of each command",
488
+ )
489
+
490
+ return generated
491
+
492
+ async def _invoke_llm(
493
+ self,
494
+ prompt: str,
495
+ tier: ModelTier,
496
+ system: str = "",
497
+ ) -> tuple[str, int, int]:
498
+ """Call LLM with the given prompt.
499
+
500
+ Uses the inherited _call_llm method from BaseWorkflow
501
+ which handles provider selection and telemetry.
502
+ """
503
+ # Use inherited _call_llm from BaseWorkflow
504
+ return await self._call_llm(
505
+ tier=tier,
506
+ system=system,
507
+ user_message=prompt,
508
+ max_tokens=4096,
509
+ )