mcli-framework 7.10.2__py3-none-any.whl → 7.11.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.

Potentially problematic release.


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

Files changed (80) hide show
  1. mcli/__init__.py +160 -0
  2. mcli/__main__.py +14 -0
  3. mcli/app/__init__.py +23 -0
  4. mcli/app/commands_cmd.py +150 -58
  5. mcli/app/main.py +21 -27
  6. mcli/app/model/__init__.py +0 -0
  7. mcli/app/video/__init__.py +5 -0
  8. mcli/chat/__init__.py +34 -0
  9. mcli/lib/__init__.py +0 -0
  10. mcli/lib/api/__init__.py +0 -0
  11. mcli/lib/auth/__init__.py +1 -0
  12. mcli/lib/config/__init__.py +1 -0
  13. mcli/lib/custom_commands.py +52 -12
  14. mcli/lib/erd/__init__.py +25 -0
  15. mcli/lib/files/__init__.py +0 -0
  16. mcli/lib/fs/__init__.py +1 -0
  17. mcli/lib/logger/__init__.py +3 -0
  18. mcli/lib/paths.py +129 -5
  19. mcli/lib/performance/__init__.py +17 -0
  20. mcli/lib/pickles/__init__.py +1 -0
  21. mcli/lib/secrets/__init__.py +10 -0
  22. mcli/lib/shell/__init__.py +0 -0
  23. mcli/lib/toml/__init__.py +1 -0
  24. mcli/lib/watcher/__init__.py +0 -0
  25. mcli/ml/__init__.py +16 -0
  26. mcli/ml/api/__init__.py +30 -0
  27. mcli/ml/api/routers/__init__.py +27 -0
  28. mcli/ml/auth/__init__.py +41 -0
  29. mcli/ml/backtesting/__init__.py +33 -0
  30. mcli/ml/cli/__init__.py +5 -0
  31. mcli/ml/config/__init__.py +33 -0
  32. mcli/ml/configs/__init__.py +16 -0
  33. mcli/ml/dashboard/__init__.py +12 -0
  34. mcli/ml/dashboard/components/__init__.py +7 -0
  35. mcli/ml/dashboard/pages/__init__.py +6 -0
  36. mcli/ml/data_ingestion/__init__.py +29 -0
  37. mcli/ml/database/__init__.py +40 -0
  38. mcli/ml/experimentation/__init__.py +29 -0
  39. mcli/ml/features/__init__.py +39 -0
  40. mcli/ml/mlops/__init__.py +19 -0
  41. mcli/ml/models/__init__.py +90 -0
  42. mcli/ml/monitoring/__init__.py +25 -0
  43. mcli/ml/optimization/__init__.py +27 -0
  44. mcli/ml/predictions/__init__.py +5 -0
  45. mcli/ml/preprocessing/__init__.py +24 -0
  46. mcli/ml/scripts/__init__.py +1 -0
  47. mcli/ml/serving/__init__.py +1 -0
  48. mcli/ml/trading/__init__.py +63 -0
  49. mcli/ml/training/__init__.py +7 -0
  50. mcli/mygroup/__init__.py +3 -0
  51. mcli/public/__init__.py +1 -0
  52. mcli/public/commands/__init__.py +2 -0
  53. mcli/self/__init__.py +3 -0
  54. mcli/self/migrate_cmd.py +261 -0
  55. mcli/self/self_cmd.py +8 -0
  56. mcli/workflow/__init__.py +0 -0
  57. mcli/workflow/daemon/__init__.py +15 -0
  58. mcli/workflow/dashboard/__init__.py +5 -0
  59. mcli/workflow/docker/__init__.py +0 -0
  60. mcli/workflow/file/__init__.py +0 -0
  61. mcli/workflow/gcloud/__init__.py +1 -0
  62. mcli/workflow/git_commit/__init__.py +0 -0
  63. mcli/workflow/interview/__init__.py +0 -0
  64. mcli/workflow/notebook/__init__.py +16 -0
  65. mcli/workflow/registry/__init__.py +0 -0
  66. mcli/workflow/repo/__init__.py +0 -0
  67. mcli/workflow/scheduler/__init__.py +25 -0
  68. mcli/workflow/search/__init__.py +0 -0
  69. mcli/workflow/secrets/__init__.py +4 -0
  70. mcli/workflow/secrets/secrets_cmd.py +192 -0
  71. mcli/workflow/sync/__init__.py +5 -0
  72. mcli/workflow/videos/__init__.py +1 -0
  73. mcli/workflow/wakatime/__init__.py +80 -0
  74. mcli/workflow/workflow.py +22 -6
  75. {mcli_framework-7.10.2.dist-info → mcli_framework-7.11.0.dist-info}/METADATA +51 -54
  76. {mcli_framework-7.10.2.dist-info → mcli_framework-7.11.0.dist-info}/RECORD +80 -12
  77. {mcli_framework-7.10.2.dist-info → mcli_framework-7.11.0.dist-info}/WHEEL +0 -0
  78. {mcli_framework-7.10.2.dist-info → mcli_framework-7.11.0.dist-info}/entry_points.txt +0 -0
  79. {mcli_framework-7.10.2.dist-info → mcli_framework-7.11.0.dist-info}/licenses/LICENSE +0 -0
  80. {mcli_framework-7.10.2.dist-info → mcli_framework-7.11.0.dist-info}/top_level.txt +0 -0
mcli/__init__.py ADDED
@@ -0,0 +1,160 @@
1
+ # logger.info("I am in mcli.__init__.py")
2
+ import sys
3
+
4
+ try:
5
+ from mcli.app import main
6
+
7
+ # from mcli.public import *
8
+ # from mcli.private import *
9
+ # Import the complete Click superset decorators
10
+ from mcli.lib.api.mcli_decorators import BOOL # mcli.BOOL - Click BOOL type
11
+ from mcli.lib.api.mcli_decorators import FLOAT # mcli.FLOAT - Click FLOAT type
12
+ from mcli.lib.api.mcli_decorators import INT # mcli.INT - Click INT type
13
+ from mcli.lib.api.mcli_decorators import STRING # mcli.STRING - Click STRING type
14
+ from mcli.lib.api.mcli_decorators import UNPROCESSED # mcli.UNPROCESSED - Click UNPROCESSED
15
+ from mcli.lib.api.mcli_decorators import UUID # mcli.UUID - Click UUID type
16
+ from mcli.lib.api.mcli_decorators import Abort # mcli.Abort - Click Abort
17
+ from mcli.lib.api.mcli_decorators import BadParameter # mcli.BadParameter - Click BadParameter
18
+ from mcli.lib.api.mcli_decorators import Choice # mcli.Choice - Click Choice type
19
+ from mcli.lib.api.mcli_decorators import File # mcli.File - Click File type
20
+ from mcli.lib.api.mcli_decorators import FloatRange # mcli.FloatRange - Click FloatRange type
21
+ from mcli.lib.api.mcli_decorators import IntRange # mcli.IntRange - Click IntRange type
22
+ from mcli.lib.api.mcli_decorators import ParamType # mcli.ParamType - Click ParamType
23
+ from mcli.lib.api.mcli_decorators import Path # mcli.Path - Click Path type
24
+ from mcli.lib.api.mcli_decorators import UsageError # mcli.UsageError - Click UsageError
25
+ from mcli.lib.api.mcli_decorators import api # @mcli.api - Legacy API decorator
26
+ from mcli.lib.api.mcli_decorators import (
27
+ api_command, # @mcli.api_command - Convenience for API endpoints
28
+ )
29
+ from mcli.lib.api.mcli_decorators import argument # @mcli.argument - Click argument decorator
30
+ from mcli.lib.api.mcli_decorators import (
31
+ background, # @mcli.background - Legacy background decorator
32
+ )
33
+ from mcli.lib.api.mcli_decorators import (
34
+ background_command, # @mcli.background_command - Convenience for background
35
+ )
36
+ from mcli.lib.api.mcli_decorators import clear # mcli.clear - Click clear
37
+ from mcli.lib.api.mcli_decorators import (
38
+ cli_with_api, # @mcli.cli_with_api - Legacy combined decorator
39
+ )
40
+ from mcli.lib.api.mcli_decorators import (
41
+ command, # @mcli.command - Complete Click command with API/background
42
+ )
43
+ from mcli.lib.api.mcli_decorators import confirm # mcli.confirm - Click confirmation
44
+ from mcli.lib.api.mcli_decorators import echo # mcli.echo - Click echo function
45
+ from mcli.lib.api.mcli_decorators import edit # mcli.edit - Click editor
46
+ from mcli.lib.api.mcli_decorators import (
47
+ format_filename, # mcli.format_filename - Click filename
48
+ )
49
+ from mcli.lib.api.mcli_decorators import get_app # mcli.get_app - Click app
50
+ from mcli.lib.api.mcli_decorators import get_app_dir # mcli.get_app_dir - Click app directory
51
+ from mcli.lib.api.mcli_decorators import (
52
+ get_binary_stream, # mcli.get_binary_stream - Click binary stream
53
+ )
54
+ from mcli.lib.api.mcli_decorators import (
55
+ get_current_context, # mcli.get_current_context - Click context
56
+ )
57
+ from mcli.lib.api.mcli_decorators import (
58
+ get_network_credentials, # mcli.get_network_credentials - Click network
59
+ )
60
+ from mcli.lib.api.mcli_decorators import get_os_args # mcli.get_os_args - Click OS args
61
+ from mcli.lib.api.mcli_decorators import (
62
+ get_terminal_size, # mcli.get_terminal_size - Click terminal size
63
+ )
64
+ from mcli.lib.api.mcli_decorators import (
65
+ get_text_stream, # mcli.get_text_stream - Click text stream
66
+ )
67
+ from mcli.lib.api.mcli_decorators import getchar # mcli.getchar - Click character input
68
+ from mcli.lib.api.mcli_decorators import (
69
+ group, # @mcli.group - Complete Click group with API support
70
+ )
71
+ from mcli.lib.api.mcli_decorators import launch # mcli.launch - Click launch
72
+ from mcli.lib.api.mcli_decorators import open_file # mcli.open_file - Click file operations
73
+ from mcli.lib.api.mcli_decorators import option # @mcli.option - Click option decorator
74
+ from mcli.lib.api.mcli_decorators import pause # mcli.pause - Click pause
75
+ from mcli.lib.api.mcli_decorators import progressbar # mcli.progressbar - Click progress bar
76
+ from mcli.lib.api.mcli_decorators import prompt # mcli.prompt - Click prompt
77
+ from mcli.lib.api.mcli_decorators import secho # mcli.secho - Click styled echo
78
+ from mcli.lib.api.mcli_decorators import style # mcli.style - Click styling
79
+ from mcli.lib.api.mcli_decorators import unstyle # mcli.unstyle - Click unstyle
80
+ from mcli.lib.api.mcli_decorators import ( # Core decorators (complete Click superset); Click re-exports (complete subsume); Click types (complete subsume); Convenience decorators; Legacy decorators (for backward compatibility); Server management; Configuration; Convenience functions
81
+ disable_api_server,
82
+ enable_api_server,
83
+ get_api_config,
84
+ health_check,
85
+ is_background_available,
86
+ is_server_running,
87
+ start_server,
88
+ status_check,
89
+ stop_server,
90
+ )
91
+
92
+ # Make everything available at the top level (complete Click subsume)
93
+ __all__ = [
94
+ "main",
95
+ # Core decorators (complete Click superset)
96
+ "command",
97
+ "group",
98
+ # Click re-exports (complete subsume)
99
+ "option",
100
+ "argument",
101
+ "echo",
102
+ "get_current_context",
103
+ "get_app",
104
+ "launch",
105
+ "open_file",
106
+ "get_os_args",
107
+ "get_binary_stream",
108
+ "get_text_stream",
109
+ "format_filename",
110
+ "getchar",
111
+ "pause",
112
+ "clear",
113
+ "style",
114
+ "unstyle",
115
+ "secho",
116
+ "edit",
117
+ "confirm",
118
+ "prompt",
119
+ "progressbar",
120
+ "get_terminal_size",
121
+ "get_app_dir",
122
+ "get_network_credentials",
123
+ # Click types (complete subsume)
124
+ "Path",
125
+ "Choice",
126
+ "IntRange",
127
+ "FloatRange",
128
+ "UNPROCESSED",
129
+ "STRING",
130
+ "INT",
131
+ "FLOAT",
132
+ "BOOL",
133
+ "UUID",
134
+ "File",
135
+ "ParamType",
136
+ "BadParameter",
137
+ "UsageError",
138
+ "Abort",
139
+ # Convenience decorators
140
+ "api_command",
141
+ "background_command",
142
+ # Legacy decorators (for backward compatibility)
143
+ "api",
144
+ "background",
145
+ "cli_with_api",
146
+ # Server management
147
+ "start_server",
148
+ "stop_server",
149
+ "is_server_running",
150
+ "is_background_available",
151
+ "get_api_config",
152
+ "enable_api_server",
153
+ "disable_api_server",
154
+ "health_check",
155
+ "status_check",
156
+ ]
157
+
158
+ except ImportError:
159
+ # from .app import main
160
+ sys.exit(1)
mcli/__main__.py ADDED
@@ -0,0 +1,14 @@
1
+ # logger.info("I am in mcli.__init__.py")
2
+
3
+ try:
4
+ from mcli.app.main import main as main_func
5
+
6
+ # from mcli.public import *
7
+ # from mcli.private import *
8
+ except ImportError:
9
+ from .app.main import main as main_func
10
+
11
+ __all__ = ["main_func"]
12
+
13
+ if __name__ == "__main__":
14
+ main_func()
mcli/app/__init__.py ADDED
@@ -0,0 +1,23 @@
1
+ # src/mcli/app/__init__.py
2
+ import importlib
3
+ from pathlib import Path
4
+
5
+ from mcli.lib.logger.logger import get_logger
6
+
7
+ logger = get_logger(__name__)
8
+
9
+ logger.info("Initializing mcli.app package")
10
+
11
+ # Import main function
12
+ try:
13
+ from .main import main
14
+
15
+ logger.info("Successfully imported main from .main")
16
+ except ImportError as e:
17
+ logger.error(f"Failed to import main: {e}")
18
+ import traceback
19
+
20
+ logger.error(f"Traceback: {traceback.format_exc()}")
21
+ raise
22
+
23
+ __all__ = ["main"]
mcli/app/commands_cmd.py CHANGED
@@ -172,38 +172,48 @@ def restore_command_state(hash_value):
172
172
  return True
173
173
 
174
174
 
175
- @click.group()
176
- def commands():
177
- """Manage and execute available commands."""
175
+ @click.group(name="workflow")
176
+ def workflow():
177
+ """Manage workflows - create, edit, import, export workflow commands."""
178
178
  pass
179
179
 
180
180
 
181
- @commands.command("list")
181
+ # For backward compatibility, keep commands as an alias
182
+ commands = workflow
183
+
184
+
185
+ @workflow.command("list")
182
186
  @click.option("--include-groups", is_flag=True, help="Include command groups in listing")
183
187
  @click.option("--daemon-only", is_flag=True, help="Show only daemon database commands")
184
188
  @click.option(
185
- "--custom-only", is_flag=True, help="Show only custom commands from ~/.mcli/commands/"
189
+ "--custom-only", is_flag=True, help="Show only custom commands from command directory"
186
190
  )
187
191
  @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
188
- def list_commands(include_groups: bool, daemon_only: bool, custom_only: bool, as_json: bool):
192
+ @click.option(
193
+ "--global", "-g", "is_global", is_flag=True, help="Use global commands (~/.mcli/commands/) instead of local (.mcli/commands/)"
194
+ )
195
+ def list_commands(include_groups: bool, daemon_only: bool, custom_only: bool, as_json: bool, is_global: bool):
189
196
  """
190
197
  List all available commands.
191
198
 
192
199
  By default, shows all discovered Click commands. Use flags to filter:
193
- - --custom-only: Show only custom commands from ~/.mcli/commands/
200
+ - --custom-only: Show only custom commands
194
201
  - --daemon-only: Show only daemon database commands
202
+ - --global/-g: Use global commands directory instead of local
195
203
 
196
204
  Examples:
197
- mcli commands list # Show all commands
198
- mcli commands list --custom-only # Show only custom commands
205
+ mcli commands list # Show all commands (local if in git repo)
206
+ mcli commands list --custom-only # Show only custom commands (local if in git repo)
207
+ mcli commands list --global # Show all global commands
208
+ mcli commands list --custom-only -g # Show only global custom commands
199
209
  mcli commands list --json # Output as JSON
200
210
  """
201
211
  from rich.table import Table
202
212
 
203
213
  try:
204
214
  if custom_only:
205
- # Show only custom commands from ~/.mcli/commands/
206
- manager = get_command_manager()
215
+ # Show only custom commands
216
+ manager = get_command_manager(global_mode=is_global)
207
217
  cmds = manager.load_all_commands()
208
218
 
209
219
  if not cmds:
@@ -232,7 +242,14 @@ def list_commands(include_groups: bool, daemon_only: bool, custom_only: bool, as
232
242
  )
233
243
 
234
244
  console.print(table)
235
- console.print(f"\n[dim]Commands directory: {manager.commands_dir}[/dim]")
245
+
246
+ # Show context information
247
+ scope = "global" if is_global or not manager.is_local else "local"
248
+ scope_color = "yellow" if scope == "local" else "cyan"
249
+ console.print(f"\n[dim]Scope: [{scope_color}]{scope}[/{scope_color}][/dim]")
250
+ if manager.is_local and manager.git_root:
251
+ console.print(f"[dim]Git repository: {manager.git_root}[/dim]")
252
+ console.print(f"[dim]Commands directory: {manager.commands_dir}[/dim]")
236
253
  console.print(f"[dim]Lockfile: {manager.lockfile_path}[/dim]")
237
254
 
238
255
  return 0
@@ -287,12 +304,19 @@ def list_commands(include_groups: bool, daemon_only: bool, custom_only: bool, as
287
304
  console.print(f"[red]Error: {e}[/red]")
288
305
 
289
306
 
290
- @commands.command("search")
307
+ @workflow.command("search")
291
308
  @click.argument("query")
292
309
  @click.option("--daemon-only", is_flag=True, help="Search only daemon database commands")
293
310
  @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
294
- def search_commands(query: str, daemon_only: bool, as_json: bool):
295
- """Search commands by name, description, or tags"""
311
+ @click.option(
312
+ "--global", "-g", "is_global", is_flag=True, help="Search global commands instead of local"
313
+ )
314
+ def search_commands(query: str, daemon_only: bool, as_json: bool, is_global: bool):
315
+ """
316
+ Search commands by name, description, or tags.
317
+
318
+ By default searches local commands (if in git repo), use --global/-g for global commands.
319
+ """
296
320
  try:
297
321
  if daemon_only:
298
322
  # Search only daemon database commands
@@ -358,7 +382,7 @@ def search_commands(query: str, daemon_only: bool, as_json: bool):
358
382
  console.print(f"[red]Error: {e}[/red]")
359
383
 
360
384
 
361
- @commands.command("execute")
385
+ @workflow.command("execute")
362
386
  @click.argument("command_name")
363
387
  @click.argument("args", nargs=-1)
364
388
  @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
@@ -388,7 +412,7 @@ def execute_command(command_name: str, args: tuple, as_json: bool, timeout: Opti
388
412
  console.print(f"[red]Error: {e}[/red]")
389
413
 
390
414
 
391
- @commands.command("info")
415
+ @workflow.command("info")
392
416
  @click.argument("command_name")
393
417
  @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
394
418
  def command_info(command_name: str, as_json: bool):
@@ -676,9 +700,9 @@ logger = get_logger()
676
700
  pass
677
701
 
678
702
 
679
- @commands.command("add")
703
+ @workflow.command("add")
680
704
  @click.argument("command_name", required=True)
681
- @click.option("--group", "-g", help="Command group (defaults to 'workflow')", default="workflow")
705
+ @click.option("--group", help="Command group (defaults to 'workflow')", default="workflow")
682
706
  @click.option("--description", "-d", help="Description for the command", default="Custom command")
683
707
  @click.option(
684
708
  "--template",
@@ -699,7 +723,10 @@ logger = get_logger()
699
723
  type=click.Choice(["bash", "zsh", "fish", "sh"], case_sensitive=False),
700
724
  help="Shell type for shell commands (defaults to $SHELL)",
701
725
  )
702
- def add_command(command_name, group, description, template, language, shell):
726
+ @click.option(
727
+ "--global", "-g", "is_global", is_flag=True, help="Add to global commands (~/.mcli/commands/) instead of local (.mcli/commands/)"
728
+ )
729
+ def add_command(command_name, group, description, template, language, shell, is_global):
703
730
  """
704
731
  Generate a new portable custom command saved to ~/.mcli/commands/.
705
732
 
@@ -749,7 +776,7 @@ def add_command(command_name, group, description, template, language, shell):
749
776
  command_group = "workflow" # Default to workflow group
750
777
 
751
778
  # Get the command manager
752
- manager = get_command_manager()
779
+ manager = get_command_manager(global_mode=is_global)
753
780
 
754
781
  # Check if command already exists
755
782
  command_file = manager.commands_dir / f"{command_name}.json"
@@ -830,29 +857,45 @@ def add_command(command_name, group, description, template, language, shell):
830
857
  )
831
858
 
832
859
  lang_display = f"{language}" if language == "python" else f"{language} ({shell})"
833
- logger.info(f"Created portable custom command: {command_name} ({lang_display})")
860
+ scope = "global" if is_global or not manager.is_local else "local"
861
+ scope_display = f"[cyan]{scope}[/cyan]" if scope == "global" else f"[yellow]{scope}[/yellow]"
862
+
863
+ logger.info(f"Created portable custom command: {command_name} ({lang_display}) [{scope}]")
834
864
  console.print(
835
- f"[green]Created portable custom command: {command_name}[/green] [dim]({lang_display})[/dim]"
865
+ f"[green]Created portable custom command: {command_name}[/green] [dim]({lang_display}) [Scope: {scope_display}][/dim]"
836
866
  )
837
867
  console.print(f"[dim]Saved to: {saved_path}[/dim]")
868
+ if manager.is_local and manager.git_root:
869
+ console.print(f"[dim]Git repository: {manager.git_root}[/dim]")
838
870
  console.print(f"[dim]Group: {command_group}[/dim]")
839
871
  console.print(f"[dim]Execute with: mcli {command_group} {command_name}[/dim]")
840
872
  console.print("[dim]Command will be automatically loaded on next mcli startup[/dim]")
841
- console.print(
842
- f"[dim]You can share this command by copying {saved_path} to another machine's ~/.mcli/commands/ directory[/dim]"
843
- )
873
+
874
+ if scope == "global":
875
+ console.print(
876
+ f"[dim]You can share this command by copying {saved_path} to another machine's ~/.mcli/commands/ directory[/dim]"
877
+ )
878
+ else:
879
+ console.print(
880
+ f"[dim]This command is local to this git repository. Use --global/-g to create global commands.[/dim]"
881
+ )
844
882
 
845
883
  return 0
846
884
 
847
885
 
848
- @commands.command("remove")
886
+ @workflow.command("remove")
849
887
  @click.argument("command_name", required=True)
850
888
  @click.option("--yes", "-y", is_flag=True, help="Skip confirmation prompt")
851
- def remove_command(command_name, yes):
889
+ @click.option(
890
+ "--global", "-g", "is_global", is_flag=True, help="Remove from global commands instead of local"
891
+ )
892
+ def remove_command(command_name, yes, is_global):
852
893
  """
853
- Remove a custom command from ~/.mcli/commands/.
894
+ Remove a custom command.
895
+
896
+ By default removes from local commands (if in git repo), use --global/-g for global commands.
854
897
  """
855
- manager = get_command_manager()
898
+ manager = get_command_manager(global_mode=is_global)
856
899
  command_file = manager.commands_dir / f"{command_name}.json"
857
900
 
858
901
  if not command_file.exists():
@@ -875,7 +918,7 @@ def remove_command(command_name, yes):
875
918
  return 1
876
919
 
877
920
 
878
- @commands.command("export")
921
+ @workflow.command("export")
879
922
  @click.argument("target", type=click.Path(), required=False)
880
923
  @click.option(
881
924
  "--script", "-s", is_flag=True, help="Export as Python script (requires command name)"
@@ -884,7 +927,10 @@ def remove_command(command_name, yes):
884
927
  "--standalone", is_flag=True, help="Make script standalone (add if __name__ == '__main__')"
885
928
  )
886
929
  @click.option("--output", "-o", type=click.Path(), help="Output file path")
887
- def export_commands(target, script, standalone, output):
930
+ @click.option(
931
+ "--global", "-g", "is_global", is_flag=True, help="Export from global commands instead of local"
932
+ )
933
+ def export_commands(target, script, standalone, output, is_global):
888
934
  """
889
935
  Export custom commands to JSON file or export a single command to Python script.
890
936
 
@@ -892,12 +938,13 @@ def export_commands(target, script, standalone, output):
892
938
  With --script/-s: Export a single command to Python script
893
939
 
894
940
  Examples:
895
- mcli commands export # Export all to commands-export.json
941
+ mcli commands export # Export all local commands
942
+ mcli commands export --global # Export all global commands
896
943
  mcli commands export my-export.json # Export all to specified file
897
944
  mcli commands export my-cmd -s # Export command to my-cmd.py
898
945
  mcli commands export my-cmd -s -o out.py --standalone
899
946
  """
900
- manager = get_command_manager()
947
+ manager = get_command_manager(global_mode=is_global)
901
948
 
902
949
  if script:
903
950
  # Export single command to Python script
@@ -972,12 +1019,12 @@ def export_commands(target, script, standalone, output):
972
1019
  return 1
973
1020
 
974
1021
 
975
- @commands.command("import")
1022
+ @workflow.command("import")
976
1023
  @click.argument("source", type=click.Path(exists=True), required=True)
977
1024
  @click.option("--script", "-s", is_flag=True, help="Import from Python script")
978
1025
  @click.option("--overwrite", is_flag=True, help="Overwrite existing commands")
979
1026
  @click.option("--name", "-n", help="Command name (for script import, defaults to script filename)")
980
- @click.option("--group", "-g", default="workflow", help="Command group (for script import)")
1027
+ @click.option("--group", default="workflow", help="Command group (for script import)")
981
1028
  @click.option("--description", "-d", help="Command description (for script import)")
982
1029
  @click.option(
983
1030
  "--interactive",
@@ -985,7 +1032,10 @@ def export_commands(target, script, standalone, output):
985
1032
  is_flag=True,
986
1033
  help="Open in $EDITOR for review/editing (for script import)",
987
1034
  )
988
- def import_commands(source, script, overwrite, name, group, description, interactive):
1035
+ @click.option(
1036
+ "--global", "-g", "is_global", is_flag=True, help="Import to global commands instead of local"
1037
+ )
1038
+ def import_commands(source, script, overwrite, name, group, description, interactive, is_global):
989
1039
  """
990
1040
  Import custom commands from JSON file or import a Python script as a command.
991
1041
 
@@ -993,13 +1043,14 @@ def import_commands(source, script, overwrite, name, group, description, interac
993
1043
  With --script/-s: Import a Python script as a command
994
1044
 
995
1045
  Examples:
996
- mcli commands import commands-export.json
1046
+ mcli commands import commands-export.json # Import to local (if in git repo)
1047
+ mcli commands import commands-export.json --global # Import to global
997
1048
  mcli commands import my_script.py -s
998
1049
  mcli commands import my_script.py -s --name custom-cmd --interactive
999
1050
  """
1000
1051
  import subprocess
1001
1052
 
1002
- manager = get_command_manager()
1053
+ manager = get_command_manager(global_mode=is_global)
1003
1054
  source_path = Path(source)
1004
1055
 
1005
1056
  if script:
@@ -1142,12 +1193,17 @@ def import_commands(source, script, overwrite, name, group, description, interac
1142
1193
  return 0
1143
1194
 
1144
1195
 
1145
- @commands.command("verify")
1146
- def verify_commands():
1196
+ @workflow.command("verify")
1197
+ @click.option(
1198
+ "--global", "-g", "is_global", is_flag=True, help="Verify global commands instead of local"
1199
+ )
1200
+ def verify_commands(is_global):
1147
1201
  """
1148
1202
  Verify that custom commands match the lockfile.
1203
+
1204
+ By default verifies local commands (if in git repo), use --global/-g for global commands.
1149
1205
  """
1150
- manager = get_command_manager()
1206
+ manager = get_command_manager(global_mode=is_global)
1151
1207
 
1152
1208
  # First, ensure lockfile is up to date
1153
1209
  manager.update_lockfile()
@@ -1180,12 +1236,17 @@ def verify_commands():
1180
1236
  return 1
1181
1237
 
1182
1238
 
1183
- @commands.command("update-lockfile")
1184
- def update_lockfile():
1239
+ @workflow.command("update-lockfile")
1240
+ @click.option(
1241
+ "--global", "-g", "is_global", is_flag=True, help="Update global lockfile instead of local"
1242
+ )
1243
+ def update_lockfile(is_global):
1185
1244
  """
1186
1245
  Update the commands lockfile with current state.
1246
+
1247
+ By default updates local lockfile (if in git repo), use --global/-g for global lockfile.
1187
1248
  """
1188
- manager = get_command_manager()
1249
+ manager = get_command_manager(global_mode=is_global)
1189
1250
 
1190
1251
  if manager.update_lockfile():
1191
1252
  console.print(f"[green]Updated lockfile: {manager.lockfile_path}[/green]")
@@ -1195,10 +1256,13 @@ def update_lockfile():
1195
1256
  return 1
1196
1257
 
1197
1258
 
1198
- @commands.command("edit")
1259
+ @workflow.command("edit")
1199
1260
  @click.argument("command_name")
1200
1261
  @click.option("--editor", "-e", help="Editor to use (defaults to $EDITOR)")
1201
- def edit_command(command_name, editor):
1262
+ @click.option(
1263
+ "--global", "-g", "is_global", is_flag=True, help="Edit global command instead of local"
1264
+ )
1265
+ def edit_command(command_name, editor, is_global):
1202
1266
  """
1203
1267
  Edit a command interactively using $EDITOR.
1204
1268
 
@@ -1206,12 +1270,13 @@ def edit_command(command_name, editor):
1206
1270
  allows you to make changes, and saves the updated version.
1207
1271
 
1208
1272
  Examples:
1209
- mcli commands edit my-command
1273
+ mcli commands edit my-command # Edit local command (if in git repo)
1274
+ mcli commands edit my-command --global # Edit global command
1210
1275
  mcli commands edit my-command --editor code
1211
1276
  """
1212
1277
  import subprocess
1213
1278
 
1214
- manager = get_command_manager()
1279
+ manager = get_command_manager(global_mode=is_global)
1215
1280
 
1216
1281
  # Load the command
1217
1282
  command_file = manager.commands_dir / f"{command_name}.json"
@@ -1294,7 +1359,7 @@ def edit_command(command_name, editor):
1294
1359
  # Moved from mcli.self for better organization
1295
1360
 
1296
1361
 
1297
- @commands.group("state")
1362
+ @workflow.group("state")
1298
1363
  def command_state():
1299
1364
  """Manage command state lockfile and history."""
1300
1365
  pass
@@ -1379,7 +1444,7 @@ def _get_store_path() -> Path:
1379
1444
  return DEFAULT_STORE_PATH
1380
1445
 
1381
1446
 
1382
- @commands.group("store")
1447
+ @workflow.group("store")
1383
1448
  def store():
1384
1449
  """Manage command store - sync ~/.mcli/commands/ to git"""
1385
1450
  pass
@@ -1466,10 +1531,19 @@ Last updated: {datetime.now().isoformat()}
1466
1531
  @store.command(name="push")
1467
1532
  @click.option("--message", "-m", help="Commit message")
1468
1533
  @click.option("--all", "-a", is_flag=True, help="Push all files (including backups)")
1469
- def push_commands(message, all):
1470
- """Push commands from ~/.mcli/commands/ to git store"""
1534
+ @click.option(
1535
+ "--global", "-g", "is_global", is_flag=True, help="Push global commands instead of local"
1536
+ )
1537
+ def push_commands(message, all, is_global):
1538
+ """
1539
+ Push commands to git store.
1540
+
1541
+ By default pushes local commands (if in git repo), use --global/-g for global commands.
1542
+ """
1471
1543
  try:
1472
1544
  store_path = _get_store_path()
1545
+ from mcli.lib.paths import get_custom_commands_dir
1546
+ COMMANDS_PATH = get_custom_commands_dir(global_mode=is_global)
1473
1547
 
1474
1548
  # Copy commands to store
1475
1549
  info(f"Copying commands from {COMMANDS_PATH} to {store_path}...")
@@ -1521,10 +1595,19 @@ def push_commands(message, all):
1521
1595
 
1522
1596
  @store.command(name="pull")
1523
1597
  @click.option("--force", "-f", is_flag=True, help="Overwrite local commands without backup")
1524
- def pull_commands(force):
1525
- """Pull commands from git store to ~/.mcli/commands/"""
1598
+ @click.option(
1599
+ "--global", "-g", "is_global", is_flag=True, help="Pull to global commands instead of local"
1600
+ )
1601
+ def pull_commands(force, is_global):
1602
+ """
1603
+ Pull commands from git store.
1604
+
1605
+ By default pulls to local commands (if in git repo), use --global/-g for global commands.
1606
+ """
1526
1607
  try:
1527
1608
  store_path = _get_store_path()
1609
+ from mcli.lib.paths import get_custom_commands_dir
1610
+ COMMANDS_PATH = get_custom_commands_dir(global_mode=is_global)
1528
1611
 
1529
1612
  # Pull from remote
1530
1613
  try:
@@ -1569,10 +1652,19 @@ def pull_commands(force):
1569
1652
 
1570
1653
  @store.command(name="sync")
1571
1654
  @click.option("--message", "-m", help="Commit message if pushing")
1572
- def sync_commands(message):
1573
- """Sync commands bidirectionally (pull then push if changes)"""
1655
+ @click.option(
1656
+ "--global", "-g", "is_global", is_flag=True, help="Sync global commands instead of local"
1657
+ )
1658
+ def sync_commands(message, is_global):
1659
+ """
1660
+ Sync commands bidirectionally (pull then push if changes).
1661
+
1662
+ By default syncs local commands (if in git repo), use --global/-g for global commands.
1663
+ """
1574
1664
  try:
1575
1665
  store_path = _get_store_path()
1666
+ from mcli.lib.paths import get_custom_commands_dir
1667
+ COMMANDS_PATH = get_custom_commands_dir(global_mode=is_global)
1576
1668
 
1577
1669
  # First pull
1578
1670
  info("Pulling latest changes...")
@@ -1769,7 +1861,7 @@ def show_command(command_name, store):
1769
1861
  # Moved from mcli.self for better organization
1770
1862
 
1771
1863
 
1772
- @commands.command("extract-workflow-commands")
1864
+ @workflow.command("extract-workflow-commands")
1773
1865
  @click.option(
1774
1866
  "--output", "-o", type=click.Path(), help="Output file (default: workflow-commands.json)"
1775
1867
  )