mcli-framework 7.1.2__tar.gz → 7.2.0__tar.gz

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.

Potentially problematic release.


This version of mcli-framework might be problematic. Click here for more details.

Files changed (319) hide show
  1. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/PKG-INFO +1 -1
  2. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/pyproject.toml +1 -1
  3. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/app/main.py +10 -0
  4. mcli_framework-7.2.0/src/mcli/lib/custom_commands.py +424 -0
  5. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/paths.py +12 -0
  6. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/dashboard/app.py +13 -13
  7. mcli_framework-7.2.0/src/mcli/ml/dashboard/app_integrated.py +2791 -0
  8. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/dashboard/app_supabase.py +46 -21
  9. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/dashboard/app_training.py +14 -14
  10. mcli_framework-7.2.0/src/mcli/ml/dashboard/components/charts.py +258 -0
  11. mcli_framework-7.2.0/src/mcli/ml/dashboard/components/metrics.py +125 -0
  12. mcli_framework-7.2.0/src/mcli/ml/dashboard/components/tables.py +228 -0
  13. mcli_framework-7.2.0/src/mcli/ml/dashboard/pages/cicd.py +382 -0
  14. mcli_framework-7.2.0/src/mcli/ml/dashboard/pages/predictions_enhanced.py +820 -0
  15. mcli_framework-7.2.0/src/mcli/ml/dashboard/pages/scrapers_and_logs.py +1060 -0
  16. mcli_framework-7.2.0/src/mcli/ml/dashboard/pages/workflows.py +533 -0
  17. mcli_framework-7.2.0/src/mcli/ml/training/train_model.py +569 -0
  18. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/self/self_cmd.py +322 -94
  19. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/politician_trading/data_sources.py +259 -1
  20. mcli_framework-7.2.0/src/mcli/workflow/politician_trading/models.py +323 -0
  21. mcli_framework-7.2.0/src/mcli/workflow/politician_trading/scrapers_corporate_registry.py +846 -0
  22. mcli_framework-7.2.0/src/mcli/workflow/politician_trading/scrapers_free_sources.py +516 -0
  23. mcli_framework-7.2.0/src/mcli/workflow/politician_trading/scrapers_third_party.py +391 -0
  24. mcli_framework-7.2.0/src/mcli/workflow/politician_trading/seed_database.py +539 -0
  25. mcli_framework-7.2.0/src/mcli/workflow/workflow.py +18 -0
  26. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli_framework.egg-info/PKG-INFO +1 -1
  27. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli_framework.egg-info/SOURCES.txt +21 -10
  28. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/cli/test_self_cmd.py +1 -1
  29. mcli_framework-7.2.0/tests/integration/test_e2e_dashboard_lsh_supabase.py +641 -0
  30. mcli_framework-7.2.0/tests/unit/test_async_process_manager.py +548 -0
  31. mcli_framework-7.2.0/tests/unit/test_custom_commands.py +339 -0
  32. mcli_framework-7.2.0/tests/unit/test_dashboard_components.py +467 -0
  33. mcli_framework-7.2.0/tests/unit/test_dashboard_pages.py +503 -0
  34. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_erd_imports.py +2 -7
  35. mcli_framework-7.2.0/tests/unit/test_git_commit_workflow.py +329 -0
  36. mcli_framework-7.2.0/tests/unit/test_scheduler.py +449 -0
  37. mcli_framework-7.2.0/tests/unit/test_supabase_pagination.py +217 -0
  38. mcli_framework-7.1.2/src/mcli/ml/dashboard/app_integrated.py +0 -912
  39. mcli_framework-7.1.2/src/mcli/workflow/daemon/api_daemon.py +0 -800
  40. mcli_framework-7.1.2/src/mcli/workflow/daemon/commands.py +0 -1196
  41. mcli_framework-7.1.2/src/mcli/workflow/dashboard/dashboard_cmd.py +0 -120
  42. mcli_framework-7.1.2/src/mcli/workflow/file/file.py +0 -100
  43. mcli_framework-7.1.2/src/mcli/workflow/git_commit/commands.py +0 -430
  44. mcli_framework-7.1.2/src/mcli/workflow/politician_trading/commands.py +0 -1939
  45. mcli_framework-7.1.2/src/mcli/workflow/politician_trading/models.py +0 -165
  46. mcli_framework-7.1.2/src/mcli/workflow/scheduler/commands.py +0 -493
  47. mcli_framework-7.1.2/src/mcli/workflow/sync/sync_cmd.py +0 -437
  48. mcli_framework-7.1.2/src/mcli/workflow/videos/videos.py +0 -242
  49. mcli_framework-7.1.2/src/mcli/workflow/workflow.py +0 -37
  50. mcli_framework-7.1.2/tests/unit/test_graph_generation.py +0 -11
  51. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/.github/dependabot.yml +0 -0
  52. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/.github/workflows/build.yml +0 -0
  53. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/.github/workflows/ci.yml +0 -0
  54. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/.github/workflows/ml-pipeline.yml +0 -0
  55. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/.github/workflows/publish-self-hosted.yml +0 -0
  56. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/.github/workflows/publish.yml +0 -0
  57. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/.github/workflows/release.yml +0 -0
  58. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/.github/workflows/security.yml +0 -0
  59. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/.github/workflows/test.yml +0 -0
  60. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/LICENSE +0 -0
  61. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/MANIFEST.in +0 -0
  62. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/PERFORMANCE_OPTIMIZATIONS.md +0 -0
  63. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/README.md +0 -0
  64. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/mcli_rust/Cargo.toml +0 -0
  65. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/mcli_rust/src/command_parser.rs +0 -0
  66. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/mcli_rust/src/file_watcher.rs +0 -0
  67. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/mcli_rust/src/lib.rs +0 -0
  68. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/mcli_rust/src/process_manager.rs +0 -0
  69. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/mcli_rust/src/tfidf.rs +0 -0
  70. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/setup.cfg +0 -0
  71. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/app/chat_cmd.py +0 -0
  72. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/app/commands_cmd.py +0 -0
  73. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/app/completion_cmd.py +0 -0
  74. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/app/completion_helpers.py +0 -0
  75. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/app/cron_test_cmd.py +0 -0
  76. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/app/logs_cmd.py +0 -0
  77. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/app/model/model.py +0 -0
  78. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/app/model_cmd.py +0 -0
  79. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/app/redis_cmd.py +0 -0
  80. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/app/video/video.py +0 -0
  81. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/app/visual_cmd.py +0 -0
  82. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/chat/chat.py +0 -0
  83. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/chat/command_rag.py +0 -0
  84. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/chat/enhanced_chat.py +0 -0
  85. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/chat/system_controller.py +0 -0
  86. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/chat/system_integration.py +0 -0
  87. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/cli.py +0 -0
  88. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/config.toml +0 -0
  89. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/api/api.py +0 -0
  90. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/api/daemon_client.py +0 -0
  91. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/api/daemon_client_local.py +0 -0
  92. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/api/daemon_decorator.py +0 -0
  93. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/api/mcli_decorators.py +0 -0
  94. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/auth/auth.py +0 -0
  95. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/auth/aws_manager.py +0 -0
  96. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/auth/azure_manager.py +0 -0
  97. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/auth/credential_manager.py +0 -0
  98. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/auth/gcp_manager.py +0 -0
  99. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/auth/key_manager.py +0 -0
  100. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/auth/mcli_manager.py +0 -0
  101. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/auth/token_manager.py +0 -0
  102. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/auth/token_util.py +0 -0
  103. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/config/config.py +0 -0
  104. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/discovery/__init__.py +0 -0
  105. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/discovery/command_discovery.py +0 -0
  106. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/erd/erd.py +0 -0
  107. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/erd/generate_graph.py +0 -0
  108. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/files/files.py +0 -0
  109. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/fs/fs.py +0 -0
  110. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/lib.py +0 -0
  111. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/logger/logger.py +0 -0
  112. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/performance/optimizer.py +0 -0
  113. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/performance/rust_bridge.py +0 -0
  114. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/performance/uvloop_config.py +0 -0
  115. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/pickles/pickles.py +0 -0
  116. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/search/cached_vectorizer.py +0 -0
  117. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/services/data_pipeline.py +0 -0
  118. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/services/lsh_client.py +0 -0
  119. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/services/redis_service.py +0 -0
  120. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/shell/shell.py +0 -0
  121. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/toml/toml.py +0 -0
  122. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/ui/styling.py +0 -0
  123. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/ui/visual_effects.py +0 -0
  124. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/lib/watcher/watcher.py +0 -0
  125. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/api/app.py +0 -0
  126. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/api/middleware.py +0 -0
  127. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/api/routers/admin_router.py +0 -0
  128. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/api/routers/auth_router.py +0 -0
  129. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/api/routers/backtest_router.py +0 -0
  130. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/api/routers/data_router.py +0 -0
  131. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/api/routers/model_router.py +0 -0
  132. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/api/routers/monitoring_router.py +0 -0
  133. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/api/routers/portfolio_router.py +0 -0
  134. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/api/routers/prediction_router.py +0 -0
  135. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/api/routers/trade_router.py +0 -0
  136. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/api/routers/websocket_router.py +0 -0
  137. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/api/schemas.py +0 -0
  138. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/auth/auth_manager.py +0 -0
  139. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/auth/models.py +0 -0
  140. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/auth/permissions.py +0 -0
  141. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/backtesting/backtest_engine.py +0 -0
  142. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/backtesting/performance_metrics.py +0 -0
  143. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/cache.py +0 -0
  144. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/cli/main.py +0 -0
  145. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/config/settings.py +0 -0
  146. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/configs/dvc_config.py +0 -0
  147. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/configs/mlflow_config.py +0 -0
  148. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/configs/mlops_manager.py +0 -0
  149. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/dashboard/cli.py +0 -0
  150. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/data_ingestion/api_connectors.py +0 -0
  151. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/data_ingestion/data_pipeline.py +0 -0
  152. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/data_ingestion/stream_processor.py +0 -0
  153. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/database/migrations/env.py +0 -0
  154. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/database/models.py +0 -0
  155. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/database/session.py +0 -0
  156. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/experimentation/ab_testing.py +0 -0
  157. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/features/ensemble_features.py +0 -0
  158. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/features/political_features.py +0 -0
  159. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/features/recommendation_engine.py +0 -0
  160. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/features/stock_features.py +0 -0
  161. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/features/test_feature_engineering.py +0 -0
  162. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/logging.py +0 -0
  163. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/mlops/data_versioning.py +0 -0
  164. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/mlops/experiment_tracker.py +0 -0
  165. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/mlops/model_serving.py +0 -0
  166. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/mlops/pipeline_orchestrator.py +0 -0
  167. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/models/base_models.py +0 -0
  168. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/models/ensemble_models.py +0 -0
  169. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/models/recommendation_models.py +0 -0
  170. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/models/test_models.py +0 -0
  171. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/monitoring/drift_detection.py +0 -0
  172. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/monitoring/metrics.py +0 -0
  173. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/optimization/portfolio_optimizer.py +0 -0
  174. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/predictions/prediction_engine.py +0 -0
  175. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/preprocessing/data_cleaners.py +0 -0
  176. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/preprocessing/feature_extractors.py +0 -0
  177. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/preprocessing/ml_pipeline.py +0 -0
  178. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/preprocessing/politician_trading_preprocessor.py +0 -0
  179. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/preprocessing/test_preprocessing.py +0 -0
  180. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/scripts/populate_sample_data.py +0 -0
  181. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/tasks.py +0 -0
  182. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/tests/test_integration.py +0 -0
  183. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/ml/tests/test_training_dashboard.py +0 -0
  184. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/mygroup/test_cmd.py +0 -0
  185. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/public/oi/oi.py +0 -0
  186. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/public/public.py +0 -0
  187. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/self/test_cmd.py +0 -0
  188. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/daemon/async_command_database.py +0 -0
  189. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/daemon/async_process_manager.py +0 -0
  190. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/daemon/client.py +0 -0
  191. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/daemon/daemon.py +0 -0
  192. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/daemon/daemon_api.py +0 -0
  193. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/daemon/enhanced_daemon.py +0 -0
  194. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/daemon/process_cli.py +0 -0
  195. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/daemon/process_manager.py +0 -0
  196. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/daemon/test_daemon.py +0 -0
  197. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/docker/docker.py +0 -0
  198. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/gcloud/config.toml +0 -0
  199. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/gcloud/gcloud.py +0 -0
  200. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/git_commit/ai_service.py +0 -0
  201. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/lsh_integration.py +0 -0
  202. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/model_service/client.py +0 -0
  203. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/model_service/download_and_run_efficient_models.py +0 -0
  204. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/model_service/lightweight_embedder.py +0 -0
  205. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/model_service/lightweight_model_server.py +0 -0
  206. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/model_service/lightweight_test.py +0 -0
  207. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/model_service/model_service.py +0 -0
  208. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/model_service/ollama_efficient_runner.py +0 -0
  209. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/model_service/pdf_processor.py +0 -0
  210. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/model_service/test_efficient_runner.py +0 -0
  211. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/model_service/test_example.py +0 -0
  212. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/model_service/test_integration.py +0 -0
  213. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/model_service/test_new_features.py +0 -0
  214. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/openai/openai.py +0 -0
  215. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/politician_trading/config.py +0 -0
  216. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/politician_trading/connectivity.py +0 -0
  217. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/politician_trading/database.py +0 -0
  218. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/politician_trading/demo.py +0 -0
  219. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/politician_trading/monitoring.py +0 -0
  220. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/politician_trading/scrapers.py +0 -0
  221. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/politician_trading/scrapers_california.py +0 -0
  222. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/politician_trading/scrapers_eu.py +0 -0
  223. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/politician_trading/scrapers_uk.py +0 -0
  224. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/politician_trading/scrapers_us_states.py +0 -0
  225. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/politician_trading/supabase_functions.py +0 -0
  226. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/politician_trading/workflow.py +0 -0
  227. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/registry/registry.py +0 -0
  228. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/repo/repo.py +0 -0
  229. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/scheduler/cron_parser.py +0 -0
  230. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/scheduler/job.py +0 -0
  231. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/scheduler/monitor.py +0 -0
  232. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/scheduler/persistence.py +0 -0
  233. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/scheduler/scheduler.py +0 -0
  234. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/sync/test_cmd.py +0 -0
  235. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli/workflow/wakatime/wakatime.py +0 -0
  236. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli_framework.egg-info/dependency_links.txt +0 -0
  237. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli_framework.egg-info/entry_points.txt +0 -0
  238. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli_framework.egg-info/requires.txt +0 -0
  239. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/src/mcli_framework.egg-info/top_level.txt +0 -0
  240. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/cli/test_all_commands.py +0 -0
  241. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/cli/test_app_logs_cmd.py +0 -0
  242. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/cli/test_app_redis_cmd.py +0 -0
  243. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/cli/test_chat_cmd.py +0 -0
  244. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/cli/test_logs_cmd.py +0 -0
  245. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/cli/test_main_app.py +0 -0
  246. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/cli/test_model_cmd.py +0 -0
  247. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/cli/test_self_cmd_commands.py +0 -0
  248. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/cli/test_self_cmd_plugins.py +0 -0
  249. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/cli/test_self_cmd_utilities.py +0 -0
  250. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/cli/test_workflow_file.py +0 -0
  251. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/cli/test_workflow_gcloud.py +0 -0
  252. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/cli/test_workflow_registry.py +0 -0
  253. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/conftest.py +0 -0
  254. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/demo_generate_graph.py +0 -0
  255. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/demo_hierarchical_transform.py +0 -0
  256. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/e2e/test_complete_workflows.py +0 -0
  257. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/e2e/test_model_workflow.py +0 -0
  258. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/e2e/test_new_user_workflow.py +0 -0
  259. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/e2e/test_update_workflow.py +0 -0
  260. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/fixtures/chat_fixtures.py +0 -0
  261. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/fixtures/cli_fixtures.py +0 -0
  262. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/fixtures/data_fixtures.py +0 -0
  263. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/fixtures/db_fixtures.py +0 -0
  264. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/fixtures/model_fixtures.py +0 -0
  265. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_agent.py +0 -0
  266. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_california_scraper.py +0 -0
  267. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_chat_client.py +0 -0
  268. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_chat_system.py +0 -0
  269. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_congress_scraper.py +0 -0
  270. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_daemon_client.py +0 -0
  271. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_daemon_server.py +0 -0
  272. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_flask_webapp.py +0 -0
  273. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_gcloud_services.py +0 -0
  274. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_lsh_client.py +0 -0
  275. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_lsh_service.py +0 -0
  276. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_ml_auth.py +0 -0
  277. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_ml_data_pipeline.py +0 -0
  278. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_ml_models.py +0 -0
  279. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_ml_pipeline.py +0 -0
  280. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_module_imports.py +0 -0
  281. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_oi_service.py +0 -0
  282. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_politician_trading.py +0 -0
  283. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_repo_operations.py +0 -0
  284. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_scheduler_integration.py +0 -0
  285. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_service_registry.py +0 -0
  286. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_uk_scraper.py +0 -0
  287. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_us_states_scraper.py +0 -0
  288. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_video_processing.py +0 -0
  289. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_wakatime_api.py +0 -0
  290. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_webapp_full.py +0 -0
  291. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_workflow.py +0 -0
  292. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/integration/test_workflow_commands.py +0 -0
  293. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/run_tests.py +0 -0
  294. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/test_harness.py +0 -0
  295. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_api_utils.py +0 -0
  296. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_auth_modules.py +0 -0
  297. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_bug_fixes.py +0 -0
  298. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_config.py +0 -0
  299. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_daemon_api.py +0 -0
  300. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_dashboard_functions.py +0 -0
  301. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_dependencies.py +0 -0
  302. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_erd_generation.py +0 -0
  303. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_erd_generic.py +0 -0
  304. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_lib_auth.py +0 -0
  305. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_lib_files.py +0 -0
  306. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_lib_utils.py +0 -0
  307. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_logger.py +0 -0
  308. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_ml_preprocessing.py +0 -0
  309. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_paths.py +0 -0
  310. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_prediction_engine.py +0 -0
  311. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_regression.py +0 -0
  312. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_scheduler_cron_parser.py +0 -0
  313. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_scheduler_job.py +0 -0
  314. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_scheduler_monitor.py +0 -0
  315. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_scheduler_persistence.py +0 -0
  316. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_self_update.py +0 -0
  317. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_toml_utils.py +0 -0
  318. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_ui_rich.py +0 -0
  319. {mcli_framework-7.1.2 → mcli_framework-7.2.0}/tests/unit/test_uv_compat.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcli-framework
3
- Version: 7.1.2
3
+ Version: 7.2.0
4
4
  Summary: 🚀 High-performance CLI framework with Rust extensions, AI chat, and stunning visuals
5
5
  Author-email: Luis Fernandez de la Vara <luis@lefv.io>
6
6
  Maintainer-email: Luis Fernandez de la Vara <luis@lefv.io>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mcli-framework"
3
- version = "7.1.2"
3
+ version = "7.2.0"
4
4
  description = "🚀 High-performance CLI framework with Rust extensions, AI chat, and stunning visuals"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.9"
@@ -423,6 +423,16 @@ def _add_lazy_commands(app: click.Group):
423
423
  app.add_command(lazy_cmd)
424
424
  logger.debug(f"Added lazy command: {cmd_name}")
425
425
 
426
+ # Load custom user commands from ~/.mcli/commands/ AFTER all groups are added
427
+ try:
428
+ from mcli.lib.custom_commands import load_custom_commands
429
+
430
+ loaded_count = load_custom_commands(app)
431
+ if loaded_count > 0:
432
+ logger.info(f"Loaded {loaded_count} custom user command(s)")
433
+ except Exception as e:
434
+ logger.debug(f"Could not load custom commands: {e}")
435
+
426
436
 
427
437
  def create_app() -> click.Group:
428
438
  """Create and configure the Click application with clean top-level commands."""
@@ -0,0 +1,424 @@
1
+ """
2
+ Custom command storage and loading for mcli.
3
+
4
+ This module provides functionality to store user-created commands in a portable
5
+ format in ~/.mcli/commands/ and automatically load them at startup.
6
+ """
7
+
8
+ import json
9
+ import importlib.util
10
+ import sys
11
+ import tempfile
12
+ from datetime import datetime
13
+ from pathlib import Path
14
+ from typing import Any, Dict, List, Optional
15
+
16
+ import click
17
+
18
+ from mcli.lib.logger.logger import get_logger
19
+ from mcli.lib.paths import get_custom_commands_dir
20
+
21
+ logger = get_logger()
22
+
23
+
24
+ class CustomCommandManager:
25
+ """Manages custom user commands stored in JSON format."""
26
+
27
+ def __init__(self):
28
+ self.commands_dir = get_custom_commands_dir()
29
+ self.loaded_commands: Dict[str, Any] = {}
30
+ self.lockfile_path = self.commands_dir / "commands.lock.json"
31
+
32
+ def save_command(
33
+ self,
34
+ name: str,
35
+ code: str,
36
+ description: str = "",
37
+ group: Optional[str] = None,
38
+ metadata: Optional[Dict[str, Any]] = None,
39
+ ) -> Path:
40
+ """
41
+ Save a custom command to the commands directory.
42
+
43
+ Args:
44
+ name: Command name
45
+ code: Python code for the command
46
+ description: Command description
47
+ group: Optional command group
48
+ metadata: Additional metadata
49
+
50
+ Returns:
51
+ Path to the saved command file
52
+ """
53
+ command_data = {
54
+ "name": name,
55
+ "code": code,
56
+ "description": description,
57
+ "group": group,
58
+ "created_at": datetime.utcnow().isoformat() + "Z",
59
+ "updated_at": datetime.utcnow().isoformat() + "Z",
60
+ "version": "1.0",
61
+ "metadata": metadata or {},
62
+ }
63
+
64
+ # Save as JSON file
65
+ command_file = self.commands_dir / f"{name}.json"
66
+ with open(command_file, "w") as f:
67
+ json.dump(command_data, f, indent=2)
68
+
69
+ logger.info(f"Saved custom command: {name} to {command_file}")
70
+
71
+ # Update lockfile
72
+ self.update_lockfile()
73
+
74
+ return command_file
75
+
76
+ def load_command(self, command_file: Path) -> Optional[Dict[str, Any]]:
77
+ """
78
+ Load a command from a JSON file.
79
+
80
+ Args:
81
+ command_file: Path to the command JSON file
82
+
83
+ Returns:
84
+ Command data dictionary or None if loading failed
85
+ """
86
+ try:
87
+ with open(command_file, "r") as f:
88
+ command_data = json.load(f)
89
+ return command_data
90
+ except Exception as e:
91
+ logger.error(f"Failed to load command from {command_file}: {e}")
92
+ return None
93
+
94
+ def load_all_commands(self) -> List[Dict[str, Any]]:
95
+ """
96
+ Load all custom commands from the commands directory.
97
+
98
+ Returns:
99
+ List of command data dictionaries
100
+ """
101
+ commands = []
102
+ for command_file in self.commands_dir.glob("*.json"):
103
+ # Skip the lockfile
104
+ if command_file.name == "commands.lock.json":
105
+ continue
106
+
107
+ command_data = self.load_command(command_file)
108
+ if command_data:
109
+ commands.append(command_data)
110
+ return commands
111
+
112
+ def delete_command(self, name: str) -> bool:
113
+ """
114
+ Delete a custom command.
115
+
116
+ Args:
117
+ name: Command name
118
+
119
+ Returns:
120
+ True if deleted successfully, False otherwise
121
+ """
122
+ command_file = self.commands_dir / f"{name}.json"
123
+ if command_file.exists():
124
+ command_file.unlink()
125
+ logger.info(f"Deleted custom command: {name}")
126
+ self.update_lockfile() # Update lockfile after deletion
127
+ return True
128
+ return False
129
+
130
+ def generate_lockfile(self) -> Dict[str, Any]:
131
+ """
132
+ Generate a lockfile containing metadata about all custom commands.
133
+
134
+ Returns:
135
+ Dictionary containing lockfile data
136
+ """
137
+ commands = self.load_all_commands()
138
+
139
+ lockfile_data = {
140
+ "version": "1.0",
141
+ "generated_at": datetime.utcnow().isoformat() + "Z",
142
+ "commands": {},
143
+ }
144
+
145
+ for command_data in commands:
146
+ name = command_data["name"]
147
+ lockfile_data["commands"][name] = {
148
+ "name": name,
149
+ "description": command_data.get("description", ""),
150
+ "group": command_data.get("group"),
151
+ "version": command_data.get("version", "1.0"),
152
+ "created_at": command_data.get("created_at", ""),
153
+ "updated_at": command_data.get("updated_at", ""),
154
+ }
155
+
156
+ return lockfile_data
157
+
158
+ def update_lockfile(self) -> bool:
159
+ """
160
+ Update the lockfile with current command state.
161
+
162
+ Returns:
163
+ True if successful, False otherwise
164
+ """
165
+ try:
166
+ lockfile_data = self.generate_lockfile()
167
+ with open(self.lockfile_path, "w") as f:
168
+ json.dump(lockfile_data, f, indent=2)
169
+ logger.debug(f"Updated lockfile: {self.lockfile_path}")
170
+ return True
171
+ except Exception as e:
172
+ logger.error(f"Failed to update lockfile: {e}")
173
+ return False
174
+
175
+ def load_lockfile(self) -> Optional[Dict[str, Any]]:
176
+ """
177
+ Load the lockfile.
178
+
179
+ Returns:
180
+ Lockfile data dictionary or None if not found
181
+ """
182
+ if not self.lockfile_path.exists():
183
+ return None
184
+
185
+ try:
186
+ with open(self.lockfile_path, "r") as f:
187
+ return json.load(f)
188
+ except Exception as e:
189
+ logger.error(f"Failed to load lockfile: {e}")
190
+ return None
191
+
192
+ def verify_lockfile(self) -> Dict[str, Any]:
193
+ """
194
+ Verify that the current command state matches the lockfile.
195
+
196
+ Returns:
197
+ Dictionary with verification results:
198
+ - 'valid': bool indicating if lockfile is valid
199
+ - 'missing': list of commands in lockfile but not in filesystem
200
+ - 'extra': list of commands in filesystem but not in lockfile
201
+ - 'modified': list of commands with different metadata
202
+ """
203
+ result = {
204
+ "valid": True,
205
+ "missing": [],
206
+ "extra": [],
207
+ "modified": [],
208
+ }
209
+
210
+ lockfile_data = self.load_lockfile()
211
+ if not lockfile_data:
212
+ result["valid"] = False
213
+ return result
214
+
215
+ current_commands = {cmd["name"]: cmd for cmd in self.load_all_commands()}
216
+ lockfile_commands = lockfile_data.get("commands", {})
217
+
218
+ # Check for missing commands (in lockfile but not in filesystem)
219
+ for name in lockfile_commands:
220
+ if name not in current_commands:
221
+ result["missing"].append(name)
222
+ result["valid"] = False
223
+
224
+ # Check for extra commands (in filesystem but not in lockfile)
225
+ for name in current_commands:
226
+ if name not in lockfile_commands:
227
+ result["extra"].append(name)
228
+ result["valid"] = False
229
+
230
+ # Check for modified commands (different metadata)
231
+ for name in set(current_commands.keys()) & set(lockfile_commands.keys()):
232
+ current = current_commands[name]
233
+ locked = lockfile_commands[name]
234
+
235
+ if current.get("updated_at") != locked.get("updated_at"):
236
+ result["modified"].append(name)
237
+ result["valid"] = False
238
+
239
+ return result
240
+
241
+ def register_command_with_click(
242
+ self, command_data: Dict[str, Any], target_group: click.Group
243
+ ) -> bool:
244
+ """
245
+ Dynamically register a custom command with a Click group.
246
+
247
+ Args:
248
+ command_data: Command data dictionary
249
+ target_group: Click group to register the command with
250
+
251
+ Returns:
252
+ True if successful, False otherwise
253
+ """
254
+ try:
255
+ name = command_data["name"]
256
+ code = command_data["code"]
257
+
258
+ # Create a temporary module to execute the command code
259
+ module_name = f"mcli_custom_{name}"
260
+
261
+ # Create a temporary file to store the code
262
+ with tempfile.NamedTemporaryFile(
263
+ mode="w", suffix=".py", delete=False
264
+ ) as temp_file:
265
+ temp_file.write(code)
266
+ temp_file_path = temp_file.name
267
+
268
+ try:
269
+ # Load the module from the temporary file
270
+ spec = importlib.util.spec_from_file_location(module_name, temp_file_path)
271
+ if spec and spec.loader:
272
+ module = importlib.util.module_from_spec(spec)
273
+ sys.modules[module_name] = module
274
+ spec.loader.exec_module(module)
275
+
276
+ # Look for a command or command group in the module
277
+ command_obj = None
278
+ for attr_name in dir(module):
279
+ attr = getattr(module, attr_name)
280
+ if isinstance(attr, (click.Command, click.Group)):
281
+ command_obj = attr
282
+ break
283
+
284
+ if command_obj:
285
+ # Register with the target group
286
+ target_group.add_command(command_obj, name=name)
287
+ self.loaded_commands[name] = command_obj
288
+ logger.info(f"Registered custom command: {name}")
289
+ return True
290
+ else:
291
+ logger.warning(
292
+ f"No Click command found in custom command: {name}"
293
+ )
294
+ return False
295
+ finally:
296
+ # Clean up temporary file
297
+ Path(temp_file_path).unlink(missing_ok=True)
298
+
299
+ except Exception as e:
300
+ logger.error(f"Failed to register custom command {name}: {e}")
301
+ return False
302
+
303
+ def export_commands(self, export_path: Path) -> bool:
304
+ """
305
+ Export all custom commands to a single JSON file.
306
+
307
+ Args:
308
+ export_path: Path to export file
309
+
310
+ Returns:
311
+ True if successful, False otherwise
312
+ """
313
+ try:
314
+ commands = self.load_all_commands()
315
+ with open(export_path, "w") as f:
316
+ json.dump(commands, f, indent=2)
317
+ logger.info(f"Exported {len(commands)} commands to {export_path}")
318
+ return True
319
+ except Exception as e:
320
+ logger.error(f"Failed to export commands: {e}")
321
+ return False
322
+
323
+ def import_commands(
324
+ self, import_path: Path, overwrite: bool = False
325
+ ) -> Dict[str, bool]:
326
+ """
327
+ Import commands from a JSON file.
328
+
329
+ Args:
330
+ import_path: Path to import file
331
+ overwrite: Whether to overwrite existing commands
332
+
333
+ Returns:
334
+ Dictionary mapping command names to success status
335
+ """
336
+ results = {}
337
+ try:
338
+ with open(import_path, "r") as f:
339
+ commands = json.load(f)
340
+
341
+ for command_data in commands:
342
+ name = command_data["name"]
343
+ command_file = self.commands_dir / f"{name}.json"
344
+
345
+ if command_file.exists() and not overwrite:
346
+ logger.warning(f"Command {name} already exists, skipping")
347
+ results[name] = False
348
+ continue
349
+
350
+ # Update timestamp
351
+ command_data["updated_at"] = datetime.utcnow().isoformat() + "Z"
352
+
353
+ with open(command_file, "w") as f:
354
+ json.dump(command_data, f, indent=2)
355
+
356
+ results[name] = True
357
+ logger.info(f"Imported command: {name}")
358
+
359
+ return results
360
+ except Exception as e:
361
+ logger.error(f"Failed to import commands: {e}")
362
+ return results
363
+
364
+
365
+ # Global instance
366
+ _command_manager: Optional[CustomCommandManager] = None
367
+
368
+
369
+ def get_command_manager() -> CustomCommandManager:
370
+ """Get the global custom command manager instance."""
371
+ global _command_manager
372
+ if _command_manager is None:
373
+ _command_manager = CustomCommandManager()
374
+ return _command_manager
375
+
376
+
377
+ def load_custom_commands(target_group: click.Group) -> int:
378
+ """
379
+ Load all custom commands and register them with the target Click group.
380
+
381
+ Args:
382
+ target_group: Click group to register commands with
383
+
384
+ Returns:
385
+ Number of commands successfully loaded
386
+ """
387
+ manager = get_command_manager()
388
+ commands = manager.load_all_commands()
389
+
390
+ loaded_count = 0
391
+ for command_data in commands:
392
+ # Check if command should be nested under a group
393
+ group_name = command_data.get("group")
394
+
395
+ if group_name:
396
+ # Find or create the group
397
+ group_cmd = target_group.commands.get(group_name)
398
+
399
+ # Handle LazyGroup - force loading
400
+ if group_cmd and hasattr(group_cmd, "_load_group"):
401
+ logger.debug(f"Loading lazy group: {group_name}")
402
+ group_cmd = group_cmd._load_group()
403
+ # Update the command in the parent group
404
+ target_group.commands[group_name] = group_cmd
405
+
406
+ if not group_cmd:
407
+ # Create the group if it doesn't exist
408
+ group_cmd = click.Group(name=group_name, help=f"{group_name.capitalize()} commands")
409
+ target_group.add_command(group_cmd)
410
+ logger.info(f"Created command group: {group_name}")
411
+
412
+ # Register the command under the group
413
+ if isinstance(group_cmd, click.Group):
414
+ if manager.register_command_with_click(command_data, group_cmd):
415
+ loaded_count += 1
416
+ else:
417
+ # Register at top level
418
+ if manager.register_command_with_click(command_data, target_group):
419
+ loaded_count += 1
420
+
421
+ if loaded_count > 0:
422
+ logger.info(f"Loaded {loaded_count} custom commands")
423
+
424
+ return loaded_count
@@ -80,3 +80,15 @@ def get_cache_dir() -> Path:
80
80
  cache_dir = get_mcli_home() / "cache"
81
81
  cache_dir.mkdir(parents=True, exist_ok=True)
82
82
  return cache_dir
83
+
84
+
85
+ def get_custom_commands_dir() -> Path:
86
+ """
87
+ Get the custom commands directory for mcli.
88
+
89
+ Returns:
90
+ Path to custom commands directory (e.g., ~/.mcli/commands), created if it doesn't exist
91
+ """
92
+ commands_dir = get_mcli_home() / "commands"
93
+ commands_dir.mkdir(parents=True, exist_ok=True)
94
+ return commands_dir
@@ -283,7 +283,7 @@ def show_overview():
283
283
  model_data = get_model_performance()
284
284
  if not model_data.empty:
285
285
  fig = px.bar(model_data, x="name", y="accuracy", title="Model Accuracy Comparison")
286
- st.plotly_chart(fig, use_container_width=True)
286
+ st.plotly_chart(fig, width="stretch", config={"responsive": True})
287
287
  else:
288
288
  st.info("No model performance data available")
289
289
 
@@ -295,7 +295,7 @@ def show_overview():
295
295
  fig = px.histogram(
296
296
  pred_data, x="confidence", title="Prediction Confidence Distribution"
297
297
  )
298
- st.plotly_chart(fig, use_container_width=True)
298
+ st.plotly_chart(fig, width="stretch", config={"responsive": True})
299
299
  else:
300
300
  st.info("No recent predictions available")
301
301
 
@@ -309,13 +309,13 @@ def show_models():
309
309
 
310
310
  if not model_data.empty:
311
311
  st.subheader("Model Performance")
312
- st.dataframe(model_data, use_container_width=True)
312
+ st.dataframe(model_data, width="stretch")
313
313
 
314
314
  # Model accuracy chart
315
315
  fig = px.line(
316
316
  model_data, x="created_at", y="accuracy", color="name", title="Model Accuracy Over Time"
317
317
  )
318
- st.plotly_chart(fig, use_container_width=True)
318
+ st.plotly_chart(fig, width="stretch", config={"responsive": True})
319
319
  else:
320
320
  st.warning("No model data available")
321
321
 
@@ -349,7 +349,7 @@ def show_predictions():
349
349
 
350
350
  # Display filtered data
351
351
  st.subheader("Filtered Predictions")
352
- st.dataframe(filtered_data, use_container_width=True)
352
+ st.dataframe(filtered_data, width="stretch")
353
353
 
354
354
  # Charts
355
355
  col1, col2 = st.columns(2)
@@ -362,7 +362,7 @@ def show_predictions():
362
362
  color="ticker",
363
363
  title="Confidence vs Predicted Return",
364
364
  )
365
- st.plotly_chart(fig, use_container_width=True)
365
+ st.plotly_chart(fig, width="stretch", config={"responsive": True})
366
366
 
367
367
  with col2:
368
368
  # Group by ticker and show average return
@@ -373,7 +373,7 @@ def show_predictions():
373
373
  y="predicted_return",
374
374
  title="Average Predicted Return by Ticker",
375
375
  )
376
- st.plotly_chart(fig, use_container_width=True)
376
+ st.plotly_chart(fig, width="stretch", config={"responsive": True})
377
377
 
378
378
  else:
379
379
  st.warning("No prediction data available")
@@ -388,7 +388,7 @@ def show_portfolios():
388
388
  if not portfolio_data.empty:
389
389
  # Portfolio metrics
390
390
  st.subheader("Portfolio Summary")
391
- st.dataframe(portfolio_data, use_container_width=True)
391
+ st.dataframe(portfolio_data, width="stretch")
392
392
 
393
393
  # Performance charts
394
394
  col1, col2 = st.columns(2)
@@ -397,7 +397,7 @@ def show_portfolios():
397
397
  fig = px.bar(
398
398
  portfolio_data, x="name", y="total_return", title="Total Return by Portfolio"
399
399
  )
400
- st.plotly_chart(fig, use_container_width=True)
400
+ st.plotly_chart(fig, width="stretch", config={"responsive": True})
401
401
 
402
402
  with col2:
403
403
  fig = px.scatter(
@@ -408,7 +408,7 @@ def show_portfolios():
408
408
  hover_data=["name"],
409
409
  title="Risk-Return Analysis",
410
410
  )
411
- st.plotly_chart(fig, use_container_width=True)
411
+ st.plotly_chart(fig, width="stretch", config={"responsive": True})
412
412
 
413
413
  else:
414
414
  st.warning("No portfolio data available")
@@ -467,7 +467,7 @@ def show_system_health():
467
467
  )
468
468
 
469
469
  fig.update_layout(height=500, title_text="System Resource Usage (24h)")
470
- st.plotly_chart(fig, use_container_width=True)
470
+ st.plotly_chart(fig, width="stretch", config={"responsive": True})
471
471
 
472
472
 
473
473
  def show_live_monitoring():
@@ -509,13 +509,13 @@ def show_live_monitoring():
509
509
  "Time": [datetime.now() - timedelta(seconds=x * 10) for x in range(5)],
510
510
  }
511
511
  )
512
- st.dataframe(new_preds, use_container_width=True)
512
+ st.dataframe(new_preds, width="stretch")
513
513
 
514
514
  # Model status
515
515
  with model_placeholder.container():
516
516
  model_data = get_model_performance()
517
517
  if not model_data.empty:
518
- st.dataframe(model_data[["name", "accuracy"]], use_container_width=True)
518
+ st.dataframe(model_data[["name", "accuracy"]], width="stretch")
519
519
 
520
520
  time.sleep(5)
521
521