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,579 @@
1
+ """Workflow commands for multi-model execution.
2
+
3
+ Copyright 2025 Smart-AI-Memory
4
+ Licensed under Fair Source License 0.9
5
+ """
6
+
7
+ import asyncio
8
+ import inspect
9
+ import json as json_mod
10
+ from pathlib import Path
11
+
12
+ from attune.config import _validate_file_path
13
+ from attune.logging_config import get_logger
14
+ from attune.workflows import get_workflow
15
+ from attune.workflows import list_workflows as get_workflow_list
16
+ from attune.workflows.config import WorkflowConfig, create_example_config
17
+
18
+ logger = get_logger(__name__)
19
+
20
+
21
+ def _extract_workflow_content(final_output):
22
+ """Extract readable content from workflow final_output.
23
+
24
+ Workflows return their results in various formats - this extracts
25
+ the actual content users want to see.
26
+ """
27
+ if final_output is None:
28
+ return None
29
+
30
+ # If it's already a string, return it
31
+ if isinstance(final_output, str):
32
+ return final_output
33
+
34
+ # If it's a dict, try to extract meaningful content
35
+ if isinstance(final_output, dict):
36
+ # Common keys that contain the main output
37
+ # formatted_report is first - preferred for security-audit and other formatted outputs
38
+ content_keys = [
39
+ "formatted_report", # Human-readable formatted output (security-audit, etc.)
40
+ "answer",
41
+ "synthesis",
42
+ "result",
43
+ "output",
44
+ "content",
45
+ "report",
46
+ "summary",
47
+ "analysis",
48
+ "review",
49
+ "documentation",
50
+ "response",
51
+ "recommendations",
52
+ "findings",
53
+ "tests",
54
+ "plan",
55
+ ]
56
+ for key in content_keys:
57
+ if final_output.get(key):
58
+ val = final_output[key]
59
+ if isinstance(val, str):
60
+ return val
61
+ if isinstance(val, dict):
62
+ # Recursively extract
63
+ return _extract_workflow_content(val)
64
+
65
+ # If no common key found, try to format the dict nicely
66
+ # Look for any string value that's substantial
67
+ for _key, val in final_output.items():
68
+ if isinstance(val, str) and len(val) > 100:
69
+ return val
70
+
71
+ # Last resort: return a formatted version
72
+ return json_mod.dumps(final_output, indent=2)
73
+
74
+ # For lists or other types, convert to string
75
+ return str(final_output)
76
+
77
+
78
+ def cmd_workflow(args):
79
+ """Multi-model workflow management and execution.
80
+
81
+ Supports listing, describing, and running workflows with tier-based models.
82
+
83
+ Args:
84
+ args: Namespace object from argparse with attributes:
85
+ - action (str): Action to perform ('list', 'describe', 'run').
86
+ - name (str | None): Workflow name (for describe/run).
87
+ - input (str | None): JSON input for workflow execution.
88
+ - provider (str | None): LLM provider override.
89
+ - json (bool): If True, output as JSON format.
90
+ - use_recommended_tier (bool): Enable tier fallback.
91
+ - write_tests (bool): For test-gen, write tests to files.
92
+ - output_dir (str | None): For test-gen, output directory.
93
+
94
+ Returns:
95
+ int | None: 0 on success, 1 on failure, None for list action.
96
+ """
97
+ action = args.action
98
+
99
+ if action == "list":
100
+ # List available workflows
101
+ workflows = get_workflow_list()
102
+
103
+ if args.json:
104
+ print(json_mod.dumps(workflows, indent=2))
105
+ else:
106
+ print("\n" + "=" * 60)
107
+ print(" MULTI-MODEL WORKFLOWS")
108
+ print("=" * 60 + "\n")
109
+
110
+ for wf in workflows:
111
+ print(f" {wf['name']:15} {wf['description']}")
112
+ stages = " → ".join(f"{s}({wf['tier_map'][s]})" for s in wf["stages"])
113
+ print(f" Stages: {stages}")
114
+ print()
115
+
116
+ print("-" * 60)
117
+ print(" Use: empathy workflow describe <name>")
118
+ print(" Use: empathy workflow run <name> [--input JSON]")
119
+ print("=" * 60 + "\n")
120
+
121
+ elif action == "describe":
122
+ # Describe a specific workflow
123
+ name = args.name
124
+ if not name:
125
+ print("Error: workflow name required")
126
+ print("Usage: empathy workflow describe <name>")
127
+ return 1
128
+
129
+ try:
130
+ workflow_cls = get_workflow(name)
131
+ provider = getattr(args, "provider", None)
132
+ workflow = workflow_cls(provider=provider)
133
+
134
+ # Get actual provider from workflow (may come from config)
135
+ actual_provider = getattr(workflow, "_provider_str", provider or "anthropic")
136
+
137
+ if args.json:
138
+ info = {
139
+ "name": workflow.name,
140
+ "description": workflow.description,
141
+ "provider": actual_provider,
142
+ "stages": workflow.stages,
143
+ "tier_map": {k: v.value for k, v in workflow.tier_map.items()},
144
+ "models": {
145
+ stage: workflow.get_model_for_tier(workflow.tier_map[stage])
146
+ for stage in workflow.stages
147
+ },
148
+ }
149
+ print(json_mod.dumps(info, indent=2))
150
+ else:
151
+ print(f"Provider: {actual_provider}")
152
+ print(workflow.describe())
153
+
154
+ except KeyError:
155
+ print(f"Error: Workflow '{name}' not found")
156
+ print("\nRun 'empathy workflow list' to see available workflows")
157
+ return 1
158
+
159
+ elif action == "run":
160
+ # Run a workflow
161
+ name = args.name
162
+ if not name:
163
+ print("Error: workflow name required")
164
+ print('Usage: empathy workflow run <name> --input \'{"key": "value"}\'')
165
+ return 1
166
+
167
+ try:
168
+ workflow_cls = get_workflow(name)
169
+
170
+ # Get provider from CLI arg, or fall back to config's default_provider
171
+ if args.provider:
172
+ provider = args.provider
173
+ else:
174
+ wf_config = WorkflowConfig.load()
175
+ provider = wf_config.default_provider
176
+
177
+ # Initialize workflow with provider and optional tier fallback
178
+ # Note: Not all workflows support enable_tier_fallback, so we check first
179
+ use_tier_fallback = getattr(args, "use_recommended_tier", False)
180
+
181
+ # Get the workflow's __init__ signature to know what params it accepts
182
+ init_sig = inspect.signature(workflow_cls.__init__)
183
+ init_params = set(init_sig.parameters.keys())
184
+
185
+ workflow_kwargs = {}
186
+
187
+ # Add provider if supported
188
+ if "provider" in init_params:
189
+ workflow_kwargs["provider"] = provider
190
+
191
+ # Add enable_tier_fallback only if the workflow supports it
192
+ if "enable_tier_fallback" in init_params and use_tier_fallback:
193
+ workflow_kwargs["enable_tier_fallback"] = use_tier_fallback
194
+
195
+ # Add health-check specific parameters
196
+ if name == "health-check" and "health_score_threshold" in init_params:
197
+ health_score_threshold = getattr(args, "health_score_threshold", 100)
198
+ workflow_kwargs["health_score_threshold"] = health_score_threshold
199
+
200
+ workflow = workflow_cls(**workflow_kwargs)
201
+
202
+ # Parse input
203
+ input_data = {}
204
+ if args.input:
205
+ input_data = json_mod.loads(args.input)
206
+
207
+ # Add test-gen specific flags to input_data (only for test-gen workflow)
208
+ if name == "test-gen":
209
+ if getattr(args, "write_tests", False):
210
+ input_data["write_tests"] = True
211
+ if getattr(args, "output_dir", None):
212
+ input_data["output_dir"] = args.output_dir
213
+
214
+ # Only print header when not in JSON mode
215
+ if not args.json:
216
+ print(f"\n Running workflow: {name} (provider: {provider})")
217
+ print("=" * 50)
218
+
219
+ # Execute workflow
220
+ result = asyncio.run(workflow.execute(**input_data))
221
+
222
+ # Extract the actual content - handle different result types
223
+ if hasattr(result, "final_output"):
224
+ output_content = _extract_workflow_content(result.final_output)
225
+ elif hasattr(result, "metadata") and isinstance(result.metadata, dict):
226
+ # Check for formatted_report in metadata (e.g., HealthCheckResult)
227
+ output_content = result.metadata.get("formatted_report")
228
+ if not output_content and hasattr(result, "summary"):
229
+ output_content = result.summary
230
+ elif hasattr(result, "summary"):
231
+ output_content = result.summary
232
+ else:
233
+ output_content = str(result)
234
+
235
+ # Get timing - handle different attribute names
236
+ duration_ms = getattr(result, "total_duration_ms", None)
237
+ if duration_ms is None and hasattr(result, "duration_seconds"):
238
+ duration_ms = int(result.duration_seconds * 1000)
239
+
240
+ # Get cost info if available (check cost_report first, then direct cost attribute)
241
+ cost_report = getattr(result, "cost_report", None)
242
+ if cost_report and hasattr(cost_report, "total_cost"):
243
+ total_cost = cost_report.total_cost
244
+ savings = getattr(cost_report, "savings", 0.0)
245
+ else:
246
+ # Fall back to direct cost attribute (e.g., CodeReviewPipelineResult)
247
+ total_cost = getattr(result, "cost", 0.0)
248
+ savings = 0.0
249
+
250
+ if args.json:
251
+ # Extract error from various result types
252
+ error = getattr(result, "error", None)
253
+ is_successful = getattr(result, "success", getattr(result, "approved", True))
254
+ if not error and not is_successful:
255
+ blockers = getattr(result, "blockers", [])
256
+ if blockers:
257
+ error = "; ".join(blockers)
258
+ else:
259
+ metadata = getattr(result, "metadata", {})
260
+ error = metadata.get("error") if isinstance(metadata, dict) else None
261
+
262
+ # JSON output includes both content and metadata
263
+ # Include final_output for programmatic access (VSCode panels, etc.)
264
+ raw_final_output = getattr(result, "final_output", None)
265
+ if raw_final_output and isinstance(raw_final_output, dict):
266
+ # Make a copy to avoid modifying the original
267
+ final_output_serializable = {}
268
+ for k, v in raw_final_output.items():
269
+ # Skip non-serializable items
270
+ if isinstance(v, set):
271
+ final_output_serializable[k] = list(v)
272
+ elif v is None or isinstance(v, str | int | float | bool | list | dict):
273
+ final_output_serializable[k] = v
274
+ else:
275
+ try:
276
+ final_output_serializable[k] = str(v)
277
+ except Exception as e: # noqa: BLE001
278
+ # INTENTIONAL: Silently skip any non-serializable objects
279
+ # This is a best-effort serialization for JSON output
280
+ # We cannot predict all possible object types users might return
281
+ logger.debug(f"Cannot serialize field {k}: {e}")
282
+ pass
283
+ else:
284
+ final_output_serializable = None
285
+
286
+ output = {
287
+ "success": is_successful,
288
+ "output": output_content,
289
+ "final_output": final_output_serializable,
290
+ "cost": total_cost,
291
+ "savings": savings,
292
+ "duration_ms": duration_ms or 0,
293
+ "error": error,
294
+ }
295
+ print(json_mod.dumps(output, indent=2))
296
+ # Display the actual results - this is what users want to see
297
+ else:
298
+ # Show tier progression if tier fallback was used
299
+ if use_tier_fallback and hasattr(workflow, "_tier_progression"):
300
+ tier_progression = workflow._tier_progression
301
+ if tier_progression:
302
+ print("\n" + "=" * 60)
303
+ print(" TIER PROGRESSION (Intelligent Fallback)")
304
+ print("=" * 60)
305
+
306
+ # Group by stage
307
+ stage_tiers: dict[str, list[tuple[str, bool]]] = {}
308
+ for stage, tier, success in tier_progression:
309
+ if stage not in stage_tiers:
310
+ stage_tiers[stage] = []
311
+ stage_tiers[stage].append((tier, success))
312
+
313
+ # Display progression for each stage
314
+ for stage, attempts in stage_tiers.items():
315
+ status = "✓" if any(success for _, success in attempts) else "✗"
316
+ print(f"\n{status} Stage: {stage}")
317
+
318
+ for idx, (tier, success) in enumerate(attempts, 1):
319
+ attempt_status = "✓ SUCCESS" if success else "✗ FAILED"
320
+ if idx == 1:
321
+ print(f" Attempt {idx}: {tier.upper():8} → {attempt_status}")
322
+ else:
323
+ prev_tier = attempts[idx - 2][0]
324
+ print(
325
+ f" Attempt {idx}: {tier.upper():8} → {attempt_status} "
326
+ f"(upgraded from {prev_tier.upper()})"
327
+ )
328
+
329
+ # Calculate cost savings (only if result has stages attribute)
330
+ if hasattr(result, "stages") and result.stages:
331
+ actual_cost = sum(stage.cost for stage in result.stages if stage.cost)
332
+ # Estimate what cost would be if all stages used PREMIUM
333
+ premium_cost = actual_cost * 3 # Conservative estimate
334
+
335
+ savings = premium_cost - actual_cost
336
+ savings_pct = (savings / premium_cost * 100) if premium_cost > 0 else 0
337
+
338
+ print("\n" + "-" * 60)
339
+ print("💰 Cost Savings:")
340
+ print(f" Actual cost: ${actual_cost:.4f}")
341
+ print(f" Premium cost: ${premium_cost:.4f} (if all PREMIUM)")
342
+ print(f" Savings: ${savings:.4f} ({savings_pct:.1f}%)")
343
+ print("=" * 60 + "\n")
344
+
345
+ # Display workflow result
346
+ # Handle different result types (success, approved, etc.)
347
+ is_successful = getattr(result, "success", getattr(result, "approved", True))
348
+ if is_successful:
349
+ if output_content:
350
+ print(f"\n{output_content}\n")
351
+ else:
352
+ print("\n✓ Workflow completed successfully.\n")
353
+ else:
354
+ # Extract error from various result types
355
+ error_msg = getattr(result, "error", None)
356
+ if not error_msg:
357
+ # Check for blockers (CodeReviewPipelineResult)
358
+ blockers = getattr(result, "blockers", [])
359
+ if blockers:
360
+ error_msg = "; ".join(blockers)
361
+ else:
362
+ # Check metadata for error
363
+ metadata = getattr(result, "metadata", {})
364
+ error_msg = (
365
+ metadata.get("error") if isinstance(metadata, dict) else None
366
+ )
367
+ error_msg = error_msg or "Unknown error"
368
+ print(f"\n✗ Workflow failed: {error_msg}\n")
369
+
370
+ except KeyError:
371
+ print(f"Error: Workflow '{name}' not found")
372
+ print("\nRun 'empathy workflow list' to see available workflows")
373
+ return 1
374
+ except json_mod.JSONDecodeError as e:
375
+ print(f"Error parsing input JSON: {e}")
376
+ print('\nExpected valid JSON, e.g.: --input \'{"key": "value"}\'')
377
+ print("Make sure to use single quotes around JSON and double quotes inside")
378
+ return 1
379
+
380
+ elif action == "config":
381
+ # Generate or show workflow configuration
382
+ config_path = Path(".attune/workflows.yaml")
383
+
384
+ if config_path.exists() and not getattr(args, "force", False):
385
+ print(f"Config already exists: {config_path}")
386
+ print("Use --force to overwrite")
387
+ print("\nCurrent configuration:")
388
+ print("-" * 40)
389
+ config = WorkflowConfig.load()
390
+ print(f" Default provider: {config.default_provider}")
391
+ if config.workflow_providers:
392
+ print(" Workflow providers:")
393
+ for wf, prov in config.workflow_providers.items():
394
+ print(f" {wf}: {prov}")
395
+ if config.custom_models:
396
+ print(" Custom models configured")
397
+ return 0
398
+
399
+ # Create config directory and file
400
+ config_path.parent.mkdir(parents=True, exist_ok=True)
401
+ validated_config_path = _validate_file_path(str(config_path))
402
+ validated_config_path.write_text(create_example_config())
403
+ print(f"✓ Created workflow config: {validated_config_path}")
404
+ print("\nEdit this file to customize:")
405
+ print(" - Default provider (anthropic, openai, ollama)")
406
+ print(" - Per-workflow provider overrides")
407
+ print(" - Custom model mappings")
408
+ print(" - Model pricing")
409
+ print("\nOr use environment variables:")
410
+ print(" EMPATHY_WORKFLOW_PROVIDER=openai")
411
+ print(" EMPATHY_MODEL_PREMIUM=gpt-5.2")
412
+
413
+ else:
414
+ print(f"Unknown action: {action}")
415
+ print("Available: list, describe, run, config")
416
+ return 1
417
+
418
+ return 0
419
+
420
+
421
+ def cmd_workflow_legacy(args):
422
+ """Interactive setup workflow (DEPRECATED).
423
+
424
+ DEPRECATED: This command is deprecated in favor of 'empathy init'.
425
+ It will be removed in version 5.0.
426
+
427
+ Guides user through initial framework configuration step by step.
428
+
429
+ Args:
430
+ args: Namespace object from argparse (no additional attributes used).
431
+
432
+ Returns:
433
+ None: Creates attune.config.yml with user's choices.
434
+ """
435
+ import warnings
436
+
437
+ warnings.warn(
438
+ "The 'workflow-setup' command is deprecated. "
439
+ "Use 'empathy init' instead. "
440
+ "This command will be removed in version 5.0.",
441
+ DeprecationWarning,
442
+ stacklevel=2,
443
+ )
444
+
445
+ print("⚠️ DEPRECATED: This command is being replaced by 'empathy init'")
446
+ print(" Please use 'empathy init' for interactive setup.")
447
+ print(" This command will be removed in version 5.0.\n")
448
+ print("=" * 60)
449
+
450
+ print("🧙 Empathy Framework Setup Workflow")
451
+ print("=" * 50)
452
+ print("\nI'll help you set up your Empathy Framework configuration.\n")
453
+
454
+ # Step 1: Use case
455
+ print("1. What's your primary use case?")
456
+ print(" [1] Software development")
457
+ print(" [2] Healthcare applications")
458
+ print(" [3] Customer support")
459
+ print(" [4] Other")
460
+
461
+ use_case_choice = input("\nYour choice (1-4): ").strip()
462
+ use_case_map = {
463
+ "1": "software_development",
464
+ "2": "healthcare",
465
+ "3": "customer_support",
466
+ "4": "general",
467
+ }
468
+ use_case = use_case_map.get(use_case_choice, "general")
469
+
470
+ # Step 2: Empathy level
471
+ print("\n2. What empathy level do you want to target?")
472
+ print(" [1] Level 1 - Reactive (basic Q&A)")
473
+ print(" [2] Level 2 - Guided (asks clarifying questions)")
474
+ print(" [3] Level 3 - Proactive (offers improvements)")
475
+ print(" [4] Level 4 - Anticipatory (predicts problems) ⭐ Recommended")
476
+ print(" [5] Level 5 - Transformative (reshapes workflows)")
477
+
478
+ level_choice = input("\nYour choice (1-5) [4]: ").strip() or "4"
479
+ target_level = int(level_choice) if level_choice in ["1", "2", "3", "4", "5"] else 4
480
+
481
+ # Step 3: LLM provider
482
+ print("\n3. Which LLM provider will you use?")
483
+ print(" [1] Anthropic Claude ⭐ Recommended")
484
+ print(" [2] OpenAI GPT-4")
485
+ print(" [3] Google Gemini (2M context)")
486
+ print(" [4] Local (Ollama)")
487
+ print(" [5] Hybrid (mix best models from each provider)")
488
+ print(" [6] Skip (configure later)")
489
+
490
+ llm_choice = input("\nYour choice (1-6) [1]: ").strip() or "1"
491
+ llm_map = {
492
+ "1": "anthropic",
493
+ "2": "openai",
494
+ "3": "google",
495
+ "4": "ollama",
496
+ "5": "hybrid",
497
+ "6": None,
498
+ }
499
+ llm_provider = llm_map.get(llm_choice, "anthropic")
500
+
501
+ # If hybrid selected, launch interactive tier selection
502
+ if llm_provider == "hybrid":
503
+ from attune.models.provider_config import configure_hybrid_interactive
504
+
505
+ configure_hybrid_interactive()
506
+ llm_provider = None # Already saved by hybrid config
507
+
508
+ # Step 4: User ID
509
+ print("\n4. What user ID should we use?")
510
+ user_id = input("User ID [default_user]: ").strip() or "default_user"
511
+
512
+ # Generate configuration
513
+ config = {
514
+ "user_id": user_id,
515
+ "target_level": target_level,
516
+ "confidence_threshold": 0.75,
517
+ "persistence_enabled": True,
518
+ "persistence_backend": "sqlite",
519
+ "persistence_path": ".empathy",
520
+ "metrics_enabled": True,
521
+ "use_case": use_case,
522
+ }
523
+
524
+ if llm_provider:
525
+ config["llm_provider"] = llm_provider
526
+
527
+ # Save configuration
528
+ output_file = "attune.config.yml"
529
+ print(f"\n5. Creating configuration file: {output_file}")
530
+
531
+ # Write YAML config
532
+ yaml_content = f"""# Empathy Framework Configuration
533
+ # Generated by setup workflow
534
+
535
+ # Core settings
536
+ user_id: "{config["user_id"]}"
537
+ target_level: {config["target_level"]}
538
+ confidence_threshold: {config["confidence_threshold"]}
539
+
540
+ # Use case
541
+ use_case: "{config["use_case"]}"
542
+
543
+ # Persistence
544
+ persistence_enabled: {str(config["persistence_enabled"]).lower()}
545
+ persistence_backend: "{config["persistence_backend"]}"
546
+ persistence_path: "{config["persistence_path"]}"
547
+
548
+ # Metrics
549
+ metrics_enabled: {str(config["metrics_enabled"]).lower()}
550
+ """
551
+
552
+ if llm_provider:
553
+ yaml_content += f"""
554
+ # LLM Provider
555
+ llm_provider: "{llm_provider}"
556
+ """
557
+
558
+ validated_output = _validate_file_path(output_file)
559
+ with open(validated_output, "w") as f:
560
+ f.write(yaml_content)
561
+
562
+ print(f" ✓ Created {validated_output}")
563
+
564
+ print("\n" + "=" * 50)
565
+ print("✅ Setup complete!")
566
+ print("\nNext steps:")
567
+ print(f" 1. Edit {output_file} to customize settings")
568
+
569
+ if llm_provider in ["anthropic", "openai", "google"]:
570
+ env_var_map = {
571
+ "anthropic": "ANTHROPIC_API_KEY",
572
+ "openai": "OPENAI_API_KEY",
573
+ "google": "GOOGLE_API_KEY",
574
+ }
575
+ env_var = env_var_map.get(llm_provider, "API_KEY")
576
+ print(f" 2. Set {env_var} environment variable")
577
+
578
+ print(" 3. Run: empathy-framework run --config attune.config.yml")
579
+ print("\nHappy empathizing! 🧠✨\n")
attune/cli/core.py ADDED
@@ -0,0 +1,32 @@
1
+ """Core utilities for Empathy Framework CLI.
2
+
3
+ Shared utilities, console configuration, and helper functions used across
4
+ all CLI command modules.
5
+
6
+ Copyright 2025 Smart-AI-Memory
7
+ Licensed under Fair Source License 0.9
8
+ """
9
+
10
+ from importlib.metadata import version as get_version
11
+
12
+ import typer
13
+ from rich.console import Console
14
+
15
+ # Shared Rich console instance for all CLI commands
16
+ console = Console()
17
+
18
+
19
+ def get_empathy_version() -> str:
20
+ """Get the installed version of empathy-framework."""
21
+ try:
22
+ return get_version("empathy-framework")
23
+ except Exception: # noqa: BLE001
24
+ # INTENTIONAL: Version detection fallback for dev installs
25
+ return "dev"
26
+
27
+
28
+ def version_callback(value: bool) -> None:
29
+ """Show version and exit."""
30
+ if value:
31
+ console.print(f"[bold blue]Empathy Framework[/bold blue] v{get_empathy_version()}")
32
+ raise typer.Exit()
@@ -0,0 +1,68 @@
1
+ """CLI parser registration.
2
+
3
+ This module coordinates parser registration for all CLI commands.
4
+
5
+ Copyright 2025 Smart-AI-Memory
6
+ Licensed under Fair Source License 0.9
7
+ """
8
+
9
+ from . import (
10
+ batch,
11
+ cache,
12
+ help,
13
+ info,
14
+ inspect,
15
+ metrics,
16
+ orchestrate,
17
+ patterns,
18
+ provider,
19
+ routing,
20
+ setup,
21
+ status,
22
+ sync,
23
+ tier,
24
+ workflow,
25
+ )
26
+
27
+
28
+ def register_all_parsers(subparsers):
29
+ """Register all command parsers.
30
+
31
+ This function is called from the main CLI entry point to set up
32
+ all subcommands and their argument parsers.
33
+
34
+ Args:
35
+ subparsers: ArgumentParser subparsers object from main parser
36
+
37
+ Note:
38
+ All 30 commands have been extracted from the monolithic cli.py
39
+ and organized into focused modules.
40
+ """
41
+ # Core commands
42
+ help.register_parsers(subparsers)
43
+ tier.register_parsers(subparsers)
44
+ info.register_parsers(subparsers)
45
+
46
+ # Pattern and state management
47
+ patterns.register_parsers(subparsers)
48
+ status.register_parsers(subparsers)
49
+
50
+ # Workflow and execution
51
+ workflow.register_parsers(subparsers)
52
+ inspect.register_parsers(subparsers)
53
+
54
+ # Provider configuration
55
+ provider.register_parsers(subparsers)
56
+
57
+ # Orchestration and sync
58
+ orchestrate.register_parsers(subparsers)
59
+ sync.register_parsers(subparsers)
60
+
61
+ # Metrics and state
62
+ metrics.register_parsers(subparsers)
63
+ cache.register_parsers(subparsers) # Cache monitoring
64
+ batch.register_parsers(subparsers) # Batch processing (50% cost savings)
65
+ routing.register_parsers(subparsers) # Adaptive routing statistics
66
+
67
+ # Setup and initialization
68
+ setup.register_parsers(subparsers)