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,413 @@
1
+ """Unified output formatting for workflows.
2
+
3
+ Provides consistent Rich-based output components for workflow results:
4
+ - WorkflowReport: Main report container with sections
5
+ - FindingsTable: Render findings as Rich Table or plain text
6
+ - MetricsPanel: Color-coded score display
7
+ - ReportSection: Individual report sections
8
+
9
+ Supports graceful fallback to plain text when Rich is unavailable.
10
+
11
+ Copyright 2025 Smart-AI-Memory
12
+ Licensed under Fair Source License 0.9
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from dataclasses import dataclass, field
18
+ from typing import TYPE_CHECKING, Any
19
+
20
+ # Rich imports with fallback
21
+ try:
22
+ from rich.console import Console
23
+ from rich.panel import Panel
24
+ from rich.table import Table
25
+ from rich.text import Text
26
+
27
+ RICH_AVAILABLE = True
28
+ except ImportError:
29
+ RICH_AVAILABLE = False
30
+ Console = None # type: ignore
31
+ Panel = None # type: ignore
32
+ Table = None # type: ignore
33
+ Text = None # type: ignore
34
+
35
+ if TYPE_CHECKING:
36
+ from rich.console import Console as ConsoleType
37
+
38
+
39
+ # =============================================================================
40
+ # DATA CLASSES
41
+ # =============================================================================
42
+
43
+
44
+ @dataclass
45
+ class Finding:
46
+ """Individual finding from a workflow."""
47
+
48
+ severity: str # "high", "medium", "low", "info"
49
+ file: str
50
+ line: int | None = None
51
+ message: str = ""
52
+ code: str | None = None
53
+
54
+ @property
55
+ def severity_icon(self) -> str:
56
+ """Get icon for severity level."""
57
+ icons = {
58
+ "high": "[red]:x:[/red]" if RICH_AVAILABLE else "X",
59
+ "medium": "[yellow]:warning:[/yellow]" if RICH_AVAILABLE else "!",
60
+ "low": "[blue]:information:[/blue]" if RICH_AVAILABLE else "i",
61
+ "info": "[dim]o[/dim]" if RICH_AVAILABLE else "o",
62
+ }
63
+ return icons.get(self.severity.lower(), "o")
64
+
65
+ @property
66
+ def location(self) -> str:
67
+ """Get file:line location string."""
68
+ if self.line:
69
+ return f"{self.file}:{self.line}"
70
+ return self.file
71
+
72
+
73
+ @dataclass
74
+ class ReportSection:
75
+ """Individual section of a workflow report."""
76
+
77
+ title: str
78
+ content: Any # str, list[Finding], dict, or Rich renderable
79
+ collapsed: bool = False
80
+ style: str = "default" # "default", "success", "warning", "error"
81
+
82
+
83
+ @dataclass
84
+ class WorkflowReport:
85
+ """Main workflow report container."""
86
+
87
+ title: str
88
+ summary: str = ""
89
+ sections: list[ReportSection] = field(default_factory=list)
90
+ score: int | None = None
91
+ level: str = "info" # "info", "success", "warning", "error"
92
+ metadata: dict[str, Any] = field(default_factory=dict)
93
+
94
+ def add_section(
95
+ self,
96
+ title: str,
97
+ content: Any,
98
+ collapsed: bool = False,
99
+ style: str = "default",
100
+ ) -> None:
101
+ """Add a section to the report."""
102
+ self.sections.append(
103
+ ReportSection(title=title, content=content, collapsed=collapsed, style=style)
104
+ )
105
+
106
+ def render(self, console: ConsoleType | None = None, use_rich: bool = True) -> str:
107
+ """Render the report.
108
+
109
+ Args:
110
+ console: Rich Console instance (optional)
111
+ use_rich: Whether to use Rich formatting
112
+
113
+ Returns:
114
+ Rendered report as string (for plain text) or prints to console (for Rich)
115
+ """
116
+ if use_rich and RICH_AVAILABLE and console is not None:
117
+ self._render_rich(console)
118
+ return ""
119
+ return self._render_plain()
120
+
121
+ def _render_rich(self, console: ConsoleType) -> None:
122
+ """Render report using Rich."""
123
+ # Header with score
124
+ header_parts = [f"[bold]{self.title}[/bold]"]
125
+ if self.score is not None:
126
+ score_panel = MetricsPanel.render_score(self.score)
127
+ console.print(score_panel)
128
+
129
+ if self.summary:
130
+ console.print(f"\n{self.summary}\n")
131
+
132
+ # Sections
133
+ for section in self.sections:
134
+ self._render_section_rich(console, section)
135
+
136
+ def _render_section_rich(self, console: ConsoleType, section: ReportSection) -> None:
137
+ """Render a single section using Rich."""
138
+ border_style = {
139
+ "success": "green",
140
+ "warning": "yellow",
141
+ "error": "red",
142
+ "default": "blue",
143
+ }.get(section.style, "blue")
144
+
145
+ if isinstance(section.content, str):
146
+ console.print(
147
+ Panel(section.content, title=section.title, border_style=border_style)
148
+ )
149
+ elif isinstance(section.content, list) and all(
150
+ isinstance(f, Finding) for f in section.content
151
+ ):
152
+ table = FindingsTable(section.content).to_rich_table()
153
+ console.print(Panel(table, title=section.title, border_style=border_style))
154
+ elif isinstance(section.content, dict):
155
+ # Render dict as key-value table
156
+ table = Table(show_header=False, box=None)
157
+ table.add_column("Key", style="cyan")
158
+ table.add_column("Value")
159
+ for key, value in section.content.items():
160
+ table.add_row(str(key), str(value))
161
+ console.print(Panel(table, title=section.title, border_style=border_style))
162
+ else:
163
+ # Try to print directly (might be a Rich renderable)
164
+ try:
165
+ console.print(
166
+ Panel(section.content, title=section.title, border_style=border_style)
167
+ )
168
+ except Exception: # noqa: BLE001
169
+ # INTENTIONAL: Graceful fallback for unknown content types
170
+ console.print(f"\n[bold]{section.title}[/bold]")
171
+ console.print(str(section.content))
172
+
173
+ def _render_plain(self) -> str:
174
+ """Render report as plain text."""
175
+ lines = []
176
+ separator = "=" * 60
177
+
178
+ # Header
179
+ lines.append(separator)
180
+ lines.append(self.title.upper())
181
+ lines.append(separator)
182
+
183
+ if self.score is not None:
184
+ level = MetricsPanel.get_level(self.score)
185
+ lines.append(f"Score: {self.score}/100 ({level.upper()})")
186
+ lines.append("")
187
+
188
+ if self.summary:
189
+ lines.append(self.summary)
190
+ lines.append("")
191
+
192
+ # Sections
193
+ for section in self.sections:
194
+ lines.append("-" * 60)
195
+ lines.append(section.title.upper())
196
+ lines.append("-" * 60)
197
+
198
+ if isinstance(section.content, str):
199
+ lines.append(section.content)
200
+ elif isinstance(section.content, list) and all(
201
+ isinstance(f, Finding) for f in section.content
202
+ ):
203
+ lines.append(FindingsTable(section.content).to_plain())
204
+ elif isinstance(section.content, dict):
205
+ for key, value in section.content.items():
206
+ lines.append(f" {key}: {value}")
207
+ else:
208
+ lines.append(str(section.content))
209
+
210
+ lines.append("")
211
+
212
+ lines.append(separator)
213
+ return "\n".join(lines)
214
+
215
+
216
+ # =============================================================================
217
+ # FINDINGS TABLE
218
+ # =============================================================================
219
+
220
+
221
+ class FindingsTable:
222
+ """Render findings as Rich Table or plain text."""
223
+
224
+ def __init__(self, findings: list[Finding]) -> None:
225
+ """Initialize with list of findings."""
226
+ self.findings = findings
227
+
228
+ def to_rich_table(self) -> Table:
229
+ """Convert findings to Rich Table."""
230
+ table = Table(show_header=True, header_style="bold magenta")
231
+ table.add_column("Severity", style="bold", width=8)
232
+ table.add_column("Location", style="cyan")
233
+ table.add_column("Message")
234
+
235
+ for finding in self.findings:
236
+ severity_style = {
237
+ "high": "red",
238
+ "medium": "yellow",
239
+ "low": "blue",
240
+ "info": "dim",
241
+ }.get(finding.severity.lower(), "white")
242
+
243
+ table.add_row(
244
+ Text(finding.severity.upper(), style=severity_style),
245
+ finding.location,
246
+ finding.message,
247
+ )
248
+
249
+ return table
250
+
251
+ def to_plain(self) -> str:
252
+ """Convert findings to plain text."""
253
+ if not self.findings:
254
+ return " No findings."
255
+
256
+ lines = []
257
+ for finding in self.findings:
258
+ lines.append(f" [{finding.severity.upper()}] {finding.location}")
259
+ if finding.message:
260
+ lines.append(f" {finding.message}")
261
+
262
+ return "\n".join(lines)
263
+
264
+
265
+ # =============================================================================
266
+ # METRICS PANEL
267
+ # =============================================================================
268
+
269
+
270
+ class MetricsPanel:
271
+ """Display score with color-coded indicator."""
272
+
273
+ @staticmethod
274
+ def get_level(score: int) -> str:
275
+ """Get level name for score."""
276
+ if score >= 85:
277
+ return "excellent"
278
+ elif score >= 70:
279
+ return "good"
280
+ elif score >= 50:
281
+ return "needs work"
282
+ return "critical"
283
+
284
+ @staticmethod
285
+ def get_style(score: int) -> str:
286
+ """Get Rich style for score."""
287
+ if score >= 85:
288
+ return "green"
289
+ elif score >= 70:
290
+ return "yellow"
291
+ elif score >= 50:
292
+ return "orange1"
293
+ return "red"
294
+
295
+ @staticmethod
296
+ def get_icon(score: int) -> str:
297
+ """Get icon for score."""
298
+ if score >= 85:
299
+ return "[green]:heavy_check_mark:[/green]"
300
+ elif score >= 70:
301
+ return "[yellow]:large_yellow_circle:[/yellow]"
302
+ elif score >= 50:
303
+ return "[orange1]:warning:[/orange1]"
304
+ return "[red]:x:[/red]"
305
+
306
+ @staticmethod
307
+ def get_plain_icon(score: int) -> str:
308
+ """Get plain text icon for score."""
309
+ if score >= 85:
310
+ return "[OK]"
311
+ elif score >= 70:
312
+ return "[--]"
313
+ elif score >= 50:
314
+ return "[!!]"
315
+ return "[XX]"
316
+
317
+ @classmethod
318
+ def render_score(cls, score: int, label: str = "Score") -> Panel:
319
+ """Render score as Rich Panel.
320
+
321
+ Args:
322
+ score: Score value (0-100)
323
+ label: Label for the score
324
+
325
+ Returns:
326
+ Rich Panel with formatted score
327
+ """
328
+ if not RICH_AVAILABLE or Panel is None:
329
+ raise RuntimeError(
330
+ "Rich library not available. "
331
+ "Install with: pip install rich"
332
+ )
333
+
334
+ style = cls.get_style(score)
335
+ icon = cls.get_icon(score)
336
+ level = cls.get_level(score)
337
+
338
+ content = f"{icon} [bold]{score}[/bold]/100 ({level.upper()})"
339
+ return Panel(content, title=f"[bold]{label}[/bold]", border_style=style)
340
+
341
+ @classmethod
342
+ def render_plain(cls, score: int, label: str = "Score") -> str:
343
+ """Render score as plain text.
344
+
345
+ Args:
346
+ score: Score value (0-100)
347
+ label: Label for the score
348
+
349
+ Returns:
350
+ Plain text score display
351
+ """
352
+ icon = cls.get_plain_icon(score)
353
+ level = cls.get_level(score)
354
+ return f"{label}: {icon} {score}/100 ({level.upper()})"
355
+
356
+
357
+ # =============================================================================
358
+ # HELPER FUNCTIONS
359
+ # =============================================================================
360
+
361
+
362
+ def format_workflow_result(
363
+ title: str,
364
+ summary: str = "",
365
+ findings: list[dict] | None = None,
366
+ score: int | None = None,
367
+ recommendations: str = "",
368
+ metadata: dict[str, Any] | None = None,
369
+ ) -> WorkflowReport:
370
+ """Create a standardized workflow report.
371
+
372
+ Args:
373
+ title: Report title
374
+ summary: Brief summary text
375
+ findings: List of finding dicts with severity, file, line, message
376
+ score: Overall score (0-100)
377
+ recommendations: Recommendations text
378
+ metadata: Additional metadata
379
+
380
+ Returns:
381
+ WorkflowReport instance
382
+ """
383
+ report = WorkflowReport(
384
+ title=title,
385
+ summary=summary,
386
+ score=score,
387
+ metadata=metadata or {},
388
+ )
389
+
390
+ if findings:
391
+ finding_objs = [
392
+ Finding(
393
+ severity=f.get("severity", "info"),
394
+ file=f.get("file", "unknown"),
395
+ line=f.get("line"),
396
+ message=f.get("message", ""),
397
+ code=f.get("code"),
398
+ )
399
+ for f in findings
400
+ ]
401
+ report.add_section("Findings", finding_objs)
402
+
403
+ if recommendations:
404
+ report.add_section("Recommendations", recommendations)
405
+
406
+ return report
407
+
408
+
409
+ def get_console() -> Console | None:
410
+ """Get Rich Console if available."""
411
+ if RICH_AVAILABLE and Console is not None:
412
+ return Console()
413
+ return None