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,590 @@
1
+ """CLI for Test Maintenance Workflow
2
+
3
+ Commands for managing the test lifecycle:
4
+ - analyze: Generate maintenance plan
5
+ - execute: Execute plan items
6
+ - auto: Auto-execute eligible items
7
+ - report: Generate test health report
8
+ - queue: Manage task queue
9
+ - crew: Run the test maintenance crew
10
+
11
+ Copyright 2025 Smart AI Memory, LLC
12
+ Licensed under Fair Source 0.9
13
+ """
14
+
15
+ import argparse
16
+ import asyncio
17
+ import json
18
+ import sys
19
+ from pathlib import Path
20
+
21
+ from ..project_index import ProjectIndex
22
+ from .test_lifecycle import TestLifecycleManager
23
+ from .test_maintenance import TestMaintenanceWorkflow
24
+ from .test_maintenance_crew import CrewConfig, TestMaintenanceCrew
25
+
26
+
27
+ def main() -> int:
28
+ """Main CLI entry point."""
29
+ parser = argparse.ArgumentParser(
30
+ description="Test Maintenance - Automatic Test Lifecycle Management",
31
+ formatter_class=argparse.RawDescriptionHelpFormatter,
32
+ epilog="""
33
+ Examples:
34
+ # Analyze test coverage and generate plan
35
+ python -m attune.workflows.test_maintenance_cli analyze
36
+
37
+ # Auto-execute eligible test generation
38
+ python -m attune.workflows.test_maintenance_cli auto
39
+
40
+ # Run the full maintenance crew
41
+ python -m attune.workflows.test_maintenance_cli crew --mode full
42
+
43
+ # View task queue
44
+ python -m attune.workflows.test_maintenance_cli queue list
45
+
46
+ # Process git hook
47
+ python -m attune.workflows.test_maintenance_cli hook post-commit
48
+ """,
49
+ )
50
+
51
+ parser.add_argument(
52
+ "--project",
53
+ "-p",
54
+ default=".",
55
+ help="Project root directory (default: current directory)",
56
+ )
57
+
58
+ parser.add_argument(
59
+ "--json",
60
+ "-j",
61
+ action="store_true",
62
+ help="Output in JSON format",
63
+ )
64
+
65
+ subparsers = parser.add_subparsers(dest="command", help="Command to run")
66
+
67
+ # analyze command
68
+ analyze_parser = subparsers.add_parser("analyze", help="Analyze and generate plan")
69
+ analyze_parser.add_argument(
70
+ "--max-items",
71
+ type=int,
72
+ default=20,
73
+ help="Maximum items in plan",
74
+ )
75
+
76
+ # execute command
77
+ execute_parser = subparsers.add_parser("execute", help="Execute plan items")
78
+ execute_parser.add_argument(
79
+ "--action",
80
+ choices=["create", "update", "review", "delete"],
81
+ help="Only execute items of this action type",
82
+ )
83
+ execute_parser.add_argument(
84
+ "--priority",
85
+ choices=["critical", "high", "medium", "low"],
86
+ help="Only execute items of this priority or higher",
87
+ )
88
+ execute_parser.add_argument(
89
+ "--dry-run",
90
+ action="store_true",
91
+ help="Don't actually execute, just show what would be done",
92
+ )
93
+
94
+ # auto command
95
+ auto_parser = subparsers.add_parser("auto", help="Auto-execute eligible items")
96
+ auto_parser.add_argument(
97
+ "--max-items",
98
+ type=int,
99
+ default=10,
100
+ help="Maximum items to process",
101
+ )
102
+ auto_parser.add_argument(
103
+ "--dry-run",
104
+ action="store_true",
105
+ help="Don't actually execute",
106
+ )
107
+
108
+ # report command
109
+ report_parser = subparsers.add_parser("report", help="Generate test health report")
110
+ report_parser.add_argument(
111
+ "--type",
112
+ choices=["health", "gaps", "staleness", "all"],
113
+ default="all",
114
+ help="Type of report",
115
+ )
116
+ report_parser.add_argument(
117
+ "--markdown",
118
+ "-m",
119
+ action="store_true",
120
+ help="Output in markdown format",
121
+ )
122
+
123
+ # queue command
124
+ queue_parser = subparsers.add_parser("queue", help="Manage task queue")
125
+ queue_subparsers = queue_parser.add_subparsers(dest="queue_action")
126
+
127
+ queue_subparsers.add_parser("list", help="List queued tasks")
128
+ queue_subparsers.add_parser("status", help="Show queue status")
129
+ queue_subparsers.add_parser("clear", help="Clear the queue")
130
+
131
+ queue_process = queue_subparsers.add_parser("process", help="Process queue")
132
+ queue_process.add_argument("--max", type=int, default=10, help="Max tasks to process")
133
+
134
+ # crew command
135
+ crew_parser = subparsers.add_parser("crew", help="Run test maintenance crew")
136
+ crew_parser.add_argument(
137
+ "--mode",
138
+ choices=["full", "analyze", "generate", "validate", "validate-only", "report"],
139
+ default="analyze",
140
+ help="Crew operation mode",
141
+ )
142
+ crew_parser.add_argument(
143
+ "--files",
144
+ nargs="*",
145
+ help="Test files for validate-only mode",
146
+ )
147
+ crew_parser.add_argument(
148
+ "--validation-optional",
149
+ action="store_true",
150
+ default=True,
151
+ help="Continue if validation fails (default: True)",
152
+ )
153
+ crew_parser.add_argument(
154
+ "--validation-timeout",
155
+ type=int,
156
+ default=120,
157
+ help="Timeout per test file in seconds (default: 120)",
158
+ )
159
+
160
+ # hook command
161
+ hook_parser = subparsers.add_parser("hook", help="Process git hooks")
162
+ hook_parser.add_argument(
163
+ "hook_type",
164
+ choices=["pre-commit", "post-commit"],
165
+ help="Type of hook",
166
+ )
167
+ hook_parser.add_argument(
168
+ "--files",
169
+ nargs="*",
170
+ help="Files involved in the hook",
171
+ )
172
+
173
+ # status command
174
+ subparsers.add_parser("status", help="Show test maintenance status")
175
+
176
+ args = parser.parse_args()
177
+
178
+ if not args.command:
179
+ parser.print_help()
180
+ return 1
181
+
182
+ # Run appropriate command
183
+ if args.command == "analyze":
184
+ return asyncio.run(cmd_analyze(args))
185
+ if args.command == "execute":
186
+ return asyncio.run(cmd_execute(args))
187
+ if args.command == "auto":
188
+ return asyncio.run(cmd_auto(args))
189
+ if args.command == "report":
190
+ return asyncio.run(cmd_report(args))
191
+ if args.command == "queue":
192
+ return asyncio.run(cmd_queue(args))
193
+ if args.command == "crew":
194
+ return asyncio.run(cmd_crew(args))
195
+ if args.command == "hook":
196
+ return asyncio.run(cmd_hook(args))
197
+ if args.command == "status":
198
+ return asyncio.run(cmd_status(args))
199
+
200
+ return 0
201
+
202
+
203
+ async def cmd_analyze(args: argparse.Namespace) -> int:
204
+ """Run analysis and generate plan."""
205
+ project_root = Path(args.project).resolve()
206
+ index = ProjectIndex(str(project_root))
207
+ if not index.load():
208
+ index.refresh()
209
+
210
+ workflow = TestMaintenanceWorkflow(str(project_root), index)
211
+ result = await workflow.run(
212
+ {
213
+ "mode": "analyze",
214
+ "max_items": args.max_items,
215
+ },
216
+ )
217
+
218
+ if args.json:
219
+ print(json.dumps(result, indent=2))
220
+ else:
221
+ _print_plan(result)
222
+
223
+ return 0
224
+
225
+
226
+ async def cmd_execute(args: argparse.Namespace) -> int:
227
+ """Execute plan items."""
228
+ project_root = Path(args.project).resolve()
229
+ index = ProjectIndex(str(project_root))
230
+ if not index.load():
231
+ index.refresh()
232
+
233
+ workflow = TestMaintenanceWorkflow(str(project_root), index)
234
+ result = await workflow.run(
235
+ {
236
+ "mode": "execute",
237
+ "dry_run": args.dry_run,
238
+ },
239
+ )
240
+
241
+ if args.json:
242
+ print(json.dumps(result, indent=2))
243
+ else:
244
+ _print_execution_result(result)
245
+
246
+ return 0
247
+
248
+
249
+ async def cmd_auto(args: argparse.Namespace) -> int:
250
+ """Auto-execute eligible items."""
251
+ project_root = Path(args.project).resolve()
252
+ index = ProjectIndex(str(project_root))
253
+ if not index.load():
254
+ index.refresh()
255
+
256
+ workflow = TestMaintenanceWorkflow(str(project_root), index)
257
+ result = await workflow.run(
258
+ {
259
+ "mode": "auto",
260
+ "max_items": args.max_items,
261
+ "dry_run": args.dry_run,
262
+ },
263
+ )
264
+
265
+ if args.json:
266
+ print(json.dumps(result, indent=2))
267
+ else:
268
+ if args.dry_run:
269
+ print("DRY RUN - No changes made")
270
+ _print_execution_result(result)
271
+
272
+ return 0
273
+
274
+
275
+ async def cmd_report(args: argparse.Namespace) -> int:
276
+ """Generate test health report."""
277
+ project_root = Path(args.project).resolve()
278
+ index = ProjectIndex(str(project_root))
279
+ if not index.load():
280
+ index.refresh()
281
+
282
+ workflow = TestMaintenanceWorkflow(str(project_root), index)
283
+ result = await workflow.run({"mode": "report"})
284
+
285
+ if args.json:
286
+ print(json.dumps(result.get("report", {}), indent=2))
287
+ else:
288
+ _print_report(result.get("report", {}), args.type, args.markdown)
289
+
290
+ return 0
291
+
292
+
293
+ async def cmd_queue(args: argparse.Namespace) -> int:
294
+ """Manage task queue."""
295
+ project_root = Path(args.project).resolve()
296
+ index = ProjectIndex(str(project_root))
297
+ if not index.load():
298
+ index.refresh()
299
+
300
+ manager = TestLifecycleManager(str(project_root), index)
301
+
302
+ if args.queue_action == "list":
303
+ queue = manager.get_queue()
304
+ if args.json:
305
+ print(json.dumps(queue, indent=2))
306
+ else:
307
+ _print_queue(queue)
308
+
309
+ elif args.queue_action == "status":
310
+ status = manager.get_status()
311
+ if args.json:
312
+ print(json.dumps(status, indent=2))
313
+ else:
314
+ _print_queue_status(status)
315
+
316
+ elif args.queue_action == "clear":
317
+ count = manager.clear_queue()
318
+ print(f"Cleared {count} tasks from queue")
319
+
320
+ elif args.queue_action == "process":
321
+ result = await manager.process_queue(max_tasks=args.max)
322
+ if args.json:
323
+ print(json.dumps(result, indent=2))
324
+ else:
325
+ print(f"Processed {result['processed']} tasks")
326
+ print(f" Succeeded: {result['succeeded']}")
327
+ print(f" Failed: {result['failed']}")
328
+
329
+ return 0
330
+
331
+
332
+ async def cmd_crew(args: argparse.Namespace) -> int:
333
+ """Run test maintenance crew."""
334
+ project_root = Path(args.project).resolve()
335
+ index = ProjectIndex(str(project_root))
336
+ if not index.load():
337
+ index.refresh()
338
+
339
+ # Configure with CLI options
340
+ config = CrewConfig(
341
+ validation_optional=getattr(args, "validation_optional", True),
342
+ validation_timeout_seconds=getattr(args, "validation_timeout", 120),
343
+ )
344
+ crew = TestMaintenanceCrew(str(project_root), index, config)
345
+
346
+ print(f"Starting Test Maintenance Crew in {args.mode} mode...")
347
+ print("=" * 60)
348
+
349
+ # Handle validate-only mode
350
+ test_files = getattr(args, "files", None)
351
+ if args.mode == "validate-only" and not test_files:
352
+ print("ERROR: validate-only mode requires --files argument")
353
+ return 1
354
+
355
+ result = await crew.run(args.mode, test_files=test_files)
356
+
357
+ if args.json:
358
+ print(json.dumps(result, indent=2))
359
+ else:
360
+ _print_crew_result(result)
361
+
362
+ return 0 if result.get("success") else 1
363
+
364
+
365
+ async def cmd_hook(args: argparse.Namespace) -> int:
366
+ """Process git hooks."""
367
+ project_root = Path(args.project).resolve()
368
+ index = ProjectIndex(str(project_root))
369
+ if not index.load():
370
+ index.refresh()
371
+
372
+ manager = TestLifecycleManager(str(project_root), index)
373
+
374
+ files = args.files or []
375
+
376
+ if args.hook_type == "pre-commit":
377
+ result = await manager.process_git_pre_commit(files)
378
+
379
+ if args.json:
380
+ print(json.dumps(result, indent=2))
381
+ elif result.get("blocking"):
382
+ print("COMMIT BLOCKED")
383
+ print("=" * 40)
384
+ for item in result["blocking"]:
385
+ print(f" {item['file']}: {item['reason']}")
386
+ return 1
387
+ elif result.get("warnings"):
388
+ print("COMMIT ALLOWED (with warnings)")
389
+ print("=" * 40)
390
+ for item in result["warnings"]:
391
+ print(f" WARNING: {item['file']}: {item['reason']}")
392
+
393
+ elif args.hook_type == "post-commit":
394
+ result = await manager.process_git_post_commit(files)
395
+
396
+ if args.json:
397
+ print(json.dumps(result, indent=2))
398
+ else:
399
+ print(f"Processed {result['changed_files']} changed files")
400
+ print(f"Queued {result['tasks_queued']} test tasks")
401
+
402
+ return 0
403
+
404
+
405
+ async def cmd_status(args: argparse.Namespace) -> int:
406
+ """Show test maintenance status."""
407
+ project_root = Path(args.project).resolve()
408
+ index = ProjectIndex(str(project_root))
409
+ if not index.load():
410
+ index.refresh()
411
+
412
+ workflow = TestMaintenanceWorkflow(str(project_root), index)
413
+ manager = TestLifecycleManager(str(project_root), index)
414
+
415
+ health = workflow.get_test_health_summary()
416
+ queue_status = manager.get_status()
417
+
418
+ if args.json:
419
+ print(
420
+ json.dumps(
421
+ {
422
+ "health": health,
423
+ "queue": queue_status,
424
+ },
425
+ indent=2,
426
+ ),
427
+ )
428
+ else:
429
+ print("TEST MAINTENANCE STATUS")
430
+ print("=" * 60)
431
+ print()
432
+ print("TEST HEALTH")
433
+ print(f" Files requiring tests: {health['files_requiring_tests']}")
434
+ print(f" Files WITH tests: {health['files_with_tests']}")
435
+ print(f" Files WITHOUT tests: {health['files_without_tests']}")
436
+ print(f" Average coverage: {health['coverage_avg']:.1f}%")
437
+ print(f" Stale tests: {health['stale_count']}")
438
+ print()
439
+ print("TASK QUEUE")
440
+ print(f" Pending tasks: {queue_status['pending']}")
441
+ print(f" Running tasks: {queue_status['running']}")
442
+ print(f" Auto-execute: {queue_status['auto_execute']}")
443
+ print()
444
+
445
+ return 0
446
+
447
+
448
+ # ===== Output Formatting =====
449
+
450
+
451
+ def _print_plan(result: dict) -> None:
452
+ """Print maintenance plan."""
453
+ plan = result.get("plan", {})
454
+ summary = plan.get("summary", {})
455
+ items = plan.get("items", [])
456
+ options = plan.get("options", [])
457
+
458
+ print("TEST MAINTENANCE PLAN")
459
+ print("=" * 60)
460
+ print()
461
+
462
+ print("SUMMARY")
463
+ print(f" Total items: {summary.get('total_items', 0)}")
464
+ print(f" Auto-executable: {summary.get('auto_executable', 0)}")
465
+ print(f" Manual required: {summary.get('manual_required', 0)}")
466
+ print()
467
+
468
+ print("BY ACTION")
469
+ for action, count in summary.get("by_action", {}).items():
470
+ if count > 0:
471
+ print(f" {action}: {count}")
472
+ print()
473
+
474
+ print("BY PRIORITY")
475
+ for priority, count in summary.get("by_priority", {}).items():
476
+ if count > 0:
477
+ print(f" {priority}: {count}")
478
+ print()
479
+
480
+ if items:
481
+ print("PLAN ITEMS")
482
+ print("-" * 60)
483
+ for i, item in enumerate(items[:10], 1):
484
+ auto = "[AUTO]" if item.get("auto_executable") else "[MANUAL]"
485
+ print(f"{i}. {auto} [{item['priority']}] {item['file_path']}")
486
+ print(f" Action: {item['action']} - {item['reason']}")
487
+ print(f" Effort: {item.get('estimated_effort', 'unknown')}")
488
+ print()
489
+
490
+ if options:
491
+ print("EXECUTION OPTIONS")
492
+ print("-" * 60)
493
+ for opt in options:
494
+ print(f" {opt['name']}")
495
+ print(f" {opt['description']}")
496
+ print(f" Command: {opt.get('command', 'N/A')}")
497
+ print()
498
+
499
+
500
+ def _print_execution_result(result: dict) -> None:
501
+ """Print execution result."""
502
+ execution = result.get("execution", {})
503
+
504
+ print("EXECUTION RESULT")
505
+ print("=" * 60)
506
+ print(f" Total: {execution.get('total', 0)}")
507
+ print(f" Succeeded: {execution.get('succeeded', 0)}")
508
+ print(f" Failed: {execution.get('failed', 0)}")
509
+ print(f" Skipped: {execution.get('skipped', 0)}")
510
+ print()
511
+
512
+ details = execution.get("details", [])
513
+ if details:
514
+ print("DETAILS")
515
+ for item in details[:10]:
516
+ status = "OK" if item.get("success") else "FAILED"
517
+ print(f" [{status}] {item['file']} - {item['action']}")
518
+
519
+
520
+ def _print_report(report: dict, report_type: str, markdown: bool) -> None:
521
+ """Print test health report."""
522
+ if report_type in ["all", "health"]:
523
+ health = report.get("health", {})
524
+ print("TEST HEALTH REPORT")
525
+ print("=" * 60)
526
+ print(
527
+ f" Health Score: {health.get('health_score', 0):.1f}/100 ({health.get('health_grade', 'N/A')})",
528
+ )
529
+ print()
530
+
531
+ for concern in health.get("concerns", []):
532
+ print(f" CONCERN: {concern}")
533
+ print()
534
+
535
+ for action in health.get("action_items", []):
536
+ print(f" [{action['priority'].upper()}] {action['action']}")
537
+
538
+
539
+ def _print_queue(queue: list) -> None:
540
+ """Print task queue."""
541
+ if not queue:
542
+ print("Queue is empty")
543
+ return
544
+
545
+ print("TASK QUEUE")
546
+ print("=" * 60)
547
+ for task in queue:
548
+ print(f" [{task['priority']}] {task['file_path']}")
549
+ print(f" Action: {task['action']}, Status: {task['status']}")
550
+ print()
551
+
552
+
553
+ def _print_queue_status(status: dict) -> None:
554
+ """Print queue status."""
555
+ print("QUEUE STATUS")
556
+ print("=" * 60)
557
+ print(f" Queue size: {status['queue_size']}")
558
+ print(f" Pending: {status['pending']}")
559
+ print(f" Running: {status['running']}")
560
+ print(f" Auto-execute: {status['auto_execute']}")
561
+ print()
562
+ print("By Priority:")
563
+ for priority, count in status.get("by_priority", {}).items():
564
+ if count > 0:
565
+ print(f" {priority}: {count}")
566
+
567
+
568
+ def _print_crew_result(result: dict) -> None:
569
+ """Print crew execution result."""
570
+ summary = result.get("summary", {})
571
+
572
+ print()
573
+ print("CREW EXECUTION COMPLETE")
574
+ print("=" * 60)
575
+ print(f" Mode: {result.get('mode')}")
576
+ print(f" Success: {result.get('success')}")
577
+ print(f" Agents executed: {summary.get('agents_executed', 0)}")
578
+ print(f" Agents succeeded: {summary.get('agents_succeeded', 0)}")
579
+ print(f" Total duration: {summary.get('total_duration_ms', 0)}ms")
580
+ print()
581
+
582
+ if summary.get("agent_results"):
583
+ print("AGENT RESULTS")
584
+ for agent in summary["agent_results"]:
585
+ status = "OK" if agent["success"] else "FAILED"
586
+ print(f" [{status}] {agent['agent']}: {agent['task']} ({agent['duration_ms']}ms)")
587
+
588
+
589
+ if __name__ == "__main__":
590
+ sys.exit(main())