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,271 @@
1
+ """Markdown Agent Parser
2
+
3
+ Parses Markdown files with YAML frontmatter into UnifiedAgentConfig.
4
+
5
+ Copyright 2025 Smart-AI-Memory
6
+ Licensed under Fair Source License 0.9
7
+ """
8
+
9
+ import logging
10
+ import re
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ from attune_llm.config.unified import ModelTier, Provider, UnifiedAgentConfig
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+ # YAML frontmatter regex pattern
19
+ FRONTMATTER_PATTERN = re.compile(
20
+ r"^---\s*\n(.*?)\n---\s*\n",
21
+ re.DOTALL,
22
+ )
23
+
24
+
25
+ class MarkdownAgentParser:
26
+ """Parser for Markdown agent definition files.
27
+
28
+ Parses files with YAML frontmatter containing agent configuration,
29
+ followed by Markdown content that becomes the system prompt.
30
+
31
+ Example file format:
32
+ ---
33
+ name: architect
34
+ description: Software architecture specialist
35
+ model: capable
36
+ tools: Read, Grep, Glob
37
+ empathy_level: 4
38
+ ---
39
+
40
+ You are an expert software architect...
41
+
42
+ Example usage:
43
+ parser = MarkdownAgentParser()
44
+ config = parser.parse_file("agents/architect.md")
45
+ """
46
+
47
+ # Mapping from string model names to ModelTier
48
+ MODEL_TIER_MAP = {
49
+ "cheap": ModelTier.CHEAP,
50
+ "haiku": ModelTier.CHEAP,
51
+ "capable": ModelTier.CAPABLE,
52
+ "sonnet": ModelTier.CAPABLE,
53
+ "premium": ModelTier.PREMIUM,
54
+ "opus": ModelTier.PREMIUM,
55
+ }
56
+
57
+ # Mapping from string provider names to Provider
58
+ PROVIDER_MAP = {
59
+ "anthropic": Provider.ANTHROPIC,
60
+ "openai": Provider.OPENAI,
61
+ "local": Provider.LOCAL,
62
+ }
63
+
64
+ def __init__(self):
65
+ """Initialize the parser."""
66
+ pass
67
+
68
+ def parse_file(self, file_path: str | Path) -> UnifiedAgentConfig:
69
+ """Parse a Markdown agent file into UnifiedAgentConfig.
70
+
71
+ Args:
72
+ file_path: Path to the Markdown agent file
73
+
74
+ Returns:
75
+ UnifiedAgentConfig instance
76
+
77
+ Raises:
78
+ FileNotFoundError: If file doesn't exist
79
+ ValueError: If file format is invalid
80
+
81
+ """
82
+ file_path = Path(file_path)
83
+
84
+ if not file_path.exists():
85
+ raise FileNotFoundError(f"Agent file not found: {file_path}")
86
+
87
+ with open(file_path, encoding="utf-8") as f:
88
+ content = f.read()
89
+
90
+ return self.parse_content(content, source=str(file_path))
91
+
92
+ def parse_content(
93
+ self,
94
+ content: str,
95
+ source: str = "unknown",
96
+ ) -> UnifiedAgentConfig:
97
+ """Parse Markdown content into UnifiedAgentConfig.
98
+
99
+ Args:
100
+ content: Markdown content with YAML frontmatter
101
+ source: Source identifier for error messages
102
+
103
+ Returns:
104
+ UnifiedAgentConfig instance
105
+
106
+ Raises:
107
+ ValueError: If content format is invalid
108
+
109
+ """
110
+ # Extract frontmatter
111
+ match = FRONTMATTER_PATTERN.match(content)
112
+
113
+ if not match:
114
+ raise ValueError(f"Invalid agent file format - missing YAML frontmatter: {source}")
115
+
116
+ frontmatter_yaml = match.group(1)
117
+ body = content[match.end() :].strip()
118
+
119
+ # Parse YAML
120
+ try:
121
+ import yaml
122
+
123
+ frontmatter = yaml.safe_load(frontmatter_yaml) or {}
124
+ except yaml.YAMLError as e:
125
+ raise ValueError(f"Invalid YAML frontmatter in {source}: {e}")
126
+
127
+ return self._create_config(frontmatter, body, source)
128
+
129
+ def _create_config(
130
+ self,
131
+ frontmatter: dict[str, Any],
132
+ body: str,
133
+ source: str,
134
+ ) -> UnifiedAgentConfig:
135
+ """Create UnifiedAgentConfig from parsed data.
136
+
137
+ Args:
138
+ frontmatter: Parsed YAML frontmatter
139
+ body: Markdown body content
140
+ source: Source identifier
141
+
142
+ Returns:
143
+ UnifiedAgentConfig instance
144
+
145
+ """
146
+ # Required field
147
+ name = frontmatter.get("name")
148
+ if not name:
149
+ raise ValueError(f"Agent file missing required 'name' field: {source}")
150
+
151
+ # Parse model tier
152
+ model_str = frontmatter.get("model", "capable").lower()
153
+ model_tier = self.MODEL_TIER_MAP.get(model_str, ModelTier.CAPABLE)
154
+
155
+ # Parse provider
156
+ provider_str = frontmatter.get("provider", "anthropic").lower()
157
+ provider = self.PROVIDER_MAP.get(provider_str, Provider.ANTHROPIC)
158
+
159
+ # Parse tools list
160
+ tools_raw = frontmatter.get("tools", [])
161
+ if isinstance(tools_raw, str):
162
+ # Handle comma-separated string
163
+ tools = [t.strip() for t in tools_raw.split(",")]
164
+ else:
165
+ tools = list(tools_raw)
166
+
167
+ # Parse capabilities
168
+ capabilities = frontmatter.get("capabilities", [])
169
+ if isinstance(capabilities, str):
170
+ capabilities = [c.strip() for c in capabilities.split(",")]
171
+
172
+ # Build config
173
+ config = UnifiedAgentConfig(
174
+ name=name,
175
+ role=frontmatter.get("role", name),
176
+ description=frontmatter.get("description", ""),
177
+ model_tier=model_tier,
178
+ model_override=frontmatter.get("model_override"),
179
+ provider=provider,
180
+ empathy_level=int(frontmatter.get("empathy_level", 4)),
181
+ memory_enabled=frontmatter.get("memory_enabled", True),
182
+ pattern_learning=frontmatter.get("pattern_learning", True),
183
+ cost_tracking=frontmatter.get("cost_tracking", True),
184
+ use_patterns=frontmatter.get("use_patterns", True),
185
+ temperature=float(frontmatter.get("temperature", 0.7)),
186
+ max_tokens=int(frontmatter.get("max_tokens", 4096)),
187
+ timeout=int(frontmatter.get("timeout", 120)),
188
+ retry_attempts=int(frontmatter.get("retry_attempts", 3)),
189
+ retry_delay=float(frontmatter.get("retry_delay", 1.0)),
190
+ system_prompt=body if body else None,
191
+ tools=tools,
192
+ capabilities=capabilities,
193
+ framework_options=frontmatter.get("framework_options", {}),
194
+ extra={
195
+ "source_file": source,
196
+ "raw_frontmatter": frontmatter,
197
+ "interaction_mode": frontmatter.get("interaction_mode", "standard"),
198
+ "socratic_config": frontmatter.get("socratic_config", {}),
199
+ },
200
+ )
201
+
202
+ logger.debug("Parsed agent config: %s from %s", name, source)
203
+ return config
204
+
205
+ def validate_file(self, file_path: str | Path) -> list[str]:
206
+ """Validate a Markdown agent file without fully parsing.
207
+
208
+ Args:
209
+ file_path: Path to validate
210
+
211
+ Returns:
212
+ List of validation error messages (empty if valid)
213
+
214
+ """
215
+ errors = []
216
+ file_path = Path(file_path)
217
+
218
+ if not file_path.exists():
219
+ return [f"File not found: {file_path}"]
220
+
221
+ try:
222
+ with open(file_path, encoding="utf-8") as f:
223
+ content = f.read()
224
+ except OSError as e:
225
+ return [f"Cannot read file: {e}"]
226
+
227
+ # Check frontmatter
228
+ match = FRONTMATTER_PATTERN.match(content)
229
+ if not match:
230
+ errors.append("Missing YAML frontmatter (must start with ---)")
231
+ return errors
232
+
233
+ # Parse YAML
234
+ try:
235
+ import yaml
236
+
237
+ frontmatter = yaml.safe_load(match.group(1)) or {}
238
+ except yaml.YAMLError as e:
239
+ errors.append(f"Invalid YAML: {e}")
240
+ return errors
241
+
242
+ # Check required fields
243
+ if not frontmatter.get("name"):
244
+ errors.append("Missing required field: name")
245
+
246
+ # Validate model tier
247
+ model = frontmatter.get("model", "").lower()
248
+ if model and model not in self.MODEL_TIER_MAP:
249
+ errors.append(
250
+ f"Invalid model '{model}'. Valid options: {', '.join(self.MODEL_TIER_MAP.keys())}"
251
+ )
252
+
253
+ # Validate provider
254
+ provider = frontmatter.get("provider", "").lower()
255
+ if provider and provider not in self.PROVIDER_MAP:
256
+ errors.append(
257
+ f"Invalid provider '{provider}'. "
258
+ f"Valid options: {', '.join(self.PROVIDER_MAP.keys())}"
259
+ )
260
+
261
+ # Validate empathy level
262
+ empathy_level = frontmatter.get("empathy_level")
263
+ if empathy_level is not None:
264
+ try:
265
+ level = int(empathy_level)
266
+ if not 1 <= level <= 5:
267
+ errors.append(f"empathy_level must be 1-5, got {level}")
268
+ except (TypeError, ValueError):
269
+ errors.append(f"empathy_level must be an integer, got {empathy_level}")
270
+
271
+ return errors
@@ -0,0 +1,307 @@
1
+ """Agent Registry
2
+
3
+ Central registry for managing available agents.
4
+
5
+ Copyright 2025 Smart-AI-Memory
6
+ Licensed under Fair Source License 0.9
7
+ """
8
+
9
+ import logging
10
+ from collections.abc import Iterator
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ from attune_llm.agents_md.loader import AgentLoader
15
+ from attune_llm.config.unified import UnifiedAgentConfig
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class AgentRegistry:
21
+ """Central registry for agent configurations.
22
+
23
+ Provides a single point of access for all available agents,
24
+ supporting both programmatic registration and directory loading.
25
+
26
+ Example:
27
+ # Create registry and load agents
28
+ registry = AgentRegistry()
29
+ registry.load_from_directory("agents/")
30
+
31
+ # Get an agent by name
32
+ architect = registry.get("architect")
33
+
34
+ # Register a custom agent
35
+ registry.register(my_custom_config)
36
+
37
+ # List all available agents
38
+ for name in registry.list_agents():
39
+ print(name)
40
+ """
41
+
42
+ _instance: "AgentRegistry | None" = None
43
+
44
+ def __init__(self):
45
+ """Initialize the registry."""
46
+ self._agents: dict[str, UnifiedAgentConfig] = {}
47
+ self._loader = AgentLoader()
48
+ self._load_paths: list[Path] = []
49
+
50
+ @classmethod
51
+ def get_instance(cls) -> "AgentRegistry":
52
+ """Get the singleton registry instance.
53
+
54
+ Returns:
55
+ The global AgentRegistry instance
56
+
57
+ """
58
+ if cls._instance is None:
59
+ cls._instance = cls()
60
+ return cls._instance
61
+
62
+ @classmethod
63
+ def reset_instance(cls) -> None:
64
+ """Reset the singleton instance (mainly for testing)."""
65
+ cls._instance = None
66
+
67
+ def register(
68
+ self,
69
+ config: UnifiedAgentConfig,
70
+ overwrite: bool = False,
71
+ ) -> None:
72
+ """Register an agent configuration.
73
+
74
+ Args:
75
+ config: Agent configuration to register
76
+ overwrite: If True, overwrite existing agent with same name
77
+
78
+ Raises:
79
+ ValueError: If agent name already exists and overwrite=False
80
+
81
+ """
82
+ if config.name in self._agents and not overwrite:
83
+ raise ValueError(
84
+ f"Agent '{config.name}' already registered. Use overwrite=True to replace."
85
+ )
86
+
87
+ self._agents[config.name] = config
88
+ logger.debug("Registered agent: %s", config.name)
89
+
90
+ def unregister(self, name: str) -> bool:
91
+ """Unregister an agent by name.
92
+
93
+ Args:
94
+ name: Agent name to remove
95
+
96
+ Returns:
97
+ True if agent was found and removed
98
+
99
+ """
100
+ if name in self._agents:
101
+ del self._agents[name]
102
+ logger.debug("Unregistered agent: %s", name)
103
+ return True
104
+ return False
105
+
106
+ def get(self, name: str) -> UnifiedAgentConfig | None:
107
+ """Get an agent configuration by name.
108
+
109
+ Args:
110
+ name: Agent name
111
+
112
+ Returns:
113
+ Agent config or None if not found
114
+
115
+ """
116
+ return self._agents.get(name)
117
+
118
+ def get_required(self, name: str) -> UnifiedAgentConfig:
119
+ """Get an agent configuration, raising if not found.
120
+
121
+ Args:
122
+ name: Agent name
123
+
124
+ Returns:
125
+ Agent configuration
126
+
127
+ Raises:
128
+ KeyError: If agent not found
129
+
130
+ """
131
+ config = self.get(name)
132
+ if config is None:
133
+ available = ", ".join(sorted(self._agents.keys()))
134
+ raise KeyError(f"Agent '{name}' not found. Available agents: {available}")
135
+ return config
136
+
137
+ def has(self, name: str) -> bool:
138
+ """Check if an agent is registered.
139
+
140
+ Args:
141
+ name: Agent name
142
+
143
+ Returns:
144
+ True if agent exists
145
+
146
+ """
147
+ return name in self._agents
148
+
149
+ def list_agents(self) -> list[str]:
150
+ """Get list of all registered agent names.
151
+
152
+ Returns:
153
+ Sorted list of agent names
154
+
155
+ """
156
+ return sorted(self._agents.keys())
157
+
158
+ def iter_agents(self) -> Iterator[UnifiedAgentConfig]:
159
+ """Iterate over all registered agents.
160
+
161
+ Yields:
162
+ Agent configurations
163
+
164
+ """
165
+ for name in sorted(self._agents.keys()):
166
+ yield self._agents[name]
167
+
168
+ def load_from_directory(
169
+ self,
170
+ directory: str | Path,
171
+ recursive: bool = False,
172
+ overwrite: bool = False,
173
+ ) -> int:
174
+ """Load agents from a directory.
175
+
176
+ Args:
177
+ directory: Directory containing .md agent files
178
+ recursive: If True, scan subdirectories
179
+ overwrite: If True, overwrite existing agents
180
+
181
+ Returns:
182
+ Number of agents loaded
183
+
184
+ """
185
+ directory = Path(directory)
186
+ self._load_paths.append(directory)
187
+
188
+ agents = self._loader.load_directory(directory, recursive=recursive)
189
+
190
+ loaded = 0
191
+ for _name, config in agents.items():
192
+ try:
193
+ self.register(config, overwrite=overwrite)
194
+ loaded += 1
195
+ except ValueError as e:
196
+ logger.warning("Skipping agent: %s", e)
197
+
198
+ logger.info("Loaded %d agent(s) from %s", loaded, directory)
199
+ return loaded
200
+
201
+ def load_from_file(
202
+ self,
203
+ file_path: str | Path,
204
+ overwrite: bool = False,
205
+ ) -> UnifiedAgentConfig:
206
+ """Load a single agent from a file.
207
+
208
+ Args:
209
+ file_path: Path to agent markdown file
210
+ overwrite: If True, overwrite existing agent
211
+
212
+ Returns:
213
+ Loaded agent configuration
214
+
215
+ """
216
+ config = self._loader.load(file_path)
217
+ self.register(config, overwrite=overwrite)
218
+ return config
219
+
220
+ def reload(self) -> int:
221
+ """Reload all agents from previously loaded directories.
222
+
223
+ Returns:
224
+ Total number of agents after reload
225
+
226
+ """
227
+ self._agents.clear()
228
+
229
+ for directory in self._load_paths:
230
+ if directory.exists():
231
+ self.load_from_directory(directory, overwrite=True)
232
+
233
+ return len(self._agents)
234
+
235
+ def get_by_role(self, role: str) -> list[UnifiedAgentConfig]:
236
+ """Get all agents with a specific role.
237
+
238
+ Args:
239
+ role: Role to filter by
240
+
241
+ Returns:
242
+ List of matching agent configs
243
+
244
+ """
245
+ role = role.lower()
246
+ return [config for config in self._agents.values() if config.role.lower() == role]
247
+
248
+ def get_by_empathy_level(
249
+ self,
250
+ min_level: int = 1,
251
+ max_level: int = 5,
252
+ ) -> list[UnifiedAgentConfig]:
253
+ """Get agents within an empathy level range.
254
+
255
+ Args:
256
+ min_level: Minimum empathy level (inclusive)
257
+ max_level: Maximum empathy level (inclusive)
258
+
259
+ Returns:
260
+ List of matching agent configs
261
+
262
+ """
263
+ return [
264
+ config
265
+ for config in self._agents.values()
266
+ if min_level <= config.empathy_level <= max_level
267
+ ]
268
+
269
+ def get_summary(self) -> dict[str, Any]:
270
+ """Get a summary of registered agents.
271
+
272
+ Returns:
273
+ Summary dictionary
274
+
275
+ """
276
+ by_role = {}
277
+ by_level = {}
278
+ by_tier = {}
279
+
280
+ for config in self._agents.values():
281
+ role = config.role
282
+ by_role[role] = by_role.get(role, 0) + 1
283
+
284
+ level = config.empathy_level
285
+ by_level[level] = by_level.get(level, 0) + 1
286
+
287
+ tier = (
288
+ config.model_tier.value
289
+ if hasattr(config.model_tier, "value")
290
+ else str(config.model_tier)
291
+ )
292
+ by_tier[tier] = by_tier.get(tier, 0) + 1
293
+
294
+ return {
295
+ "total_agents": len(self._agents),
296
+ "agent_names": self.list_agents(),
297
+ "by_role": by_role,
298
+ "by_empathy_level": by_level,
299
+ "by_model_tier": by_tier,
300
+ "load_paths": [str(p) for p in self._load_paths],
301
+ }
302
+
303
+ def clear(self) -> None:
304
+ """Clear all registered agents."""
305
+ self._agents.clear()
306
+ self._load_paths.clear()
307
+ logger.debug("Cleared agent registry")