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.
- mcli/__init__.py +160 -0
- mcli/__main__.py +14 -0
- mcli/app/__init__.py +23 -0
- mcli/app/commands_cmd.py +150 -58
- mcli/app/main.py +21 -27
- mcli/app/model/__init__.py +0 -0
- mcli/app/video/__init__.py +5 -0
- mcli/chat/__init__.py +34 -0
- mcli/lib/__init__.py +0 -0
- mcli/lib/api/__init__.py +0 -0
- mcli/lib/auth/__init__.py +1 -0
- mcli/lib/config/__init__.py +1 -0
- mcli/lib/custom_commands.py +52 -12
- mcli/lib/erd/__init__.py +25 -0
- mcli/lib/files/__init__.py +0 -0
- mcli/lib/fs/__init__.py +1 -0
- mcli/lib/logger/__init__.py +3 -0
- mcli/lib/paths.py +129 -5
- mcli/lib/performance/__init__.py +17 -0
- mcli/lib/pickles/__init__.py +1 -0
- mcli/lib/secrets/__init__.py +10 -0
- mcli/lib/shell/__init__.py +0 -0
- mcli/lib/toml/__init__.py +1 -0
- mcli/lib/watcher/__init__.py +0 -0
- mcli/ml/__init__.py +16 -0
- mcli/ml/api/__init__.py +30 -0
- mcli/ml/api/routers/__init__.py +27 -0
- mcli/ml/auth/__init__.py +41 -0
- mcli/ml/backtesting/__init__.py +33 -0
- mcli/ml/cli/__init__.py +5 -0
- mcli/ml/config/__init__.py +33 -0
- mcli/ml/configs/__init__.py +16 -0
- mcli/ml/dashboard/__init__.py +12 -0
- mcli/ml/dashboard/components/__init__.py +7 -0
- mcli/ml/dashboard/pages/__init__.py +6 -0
- mcli/ml/data_ingestion/__init__.py +29 -0
- mcli/ml/database/__init__.py +40 -0
- mcli/ml/experimentation/__init__.py +29 -0
- mcli/ml/features/__init__.py +39 -0
- mcli/ml/mlops/__init__.py +19 -0
- mcli/ml/models/__init__.py +90 -0
- mcli/ml/monitoring/__init__.py +25 -0
- mcli/ml/optimization/__init__.py +27 -0
- mcli/ml/predictions/__init__.py +5 -0
- mcli/ml/preprocessing/__init__.py +24 -0
- mcli/ml/scripts/__init__.py +1 -0
- mcli/ml/serving/__init__.py +1 -0
- mcli/ml/trading/__init__.py +63 -0
- mcli/ml/training/__init__.py +7 -0
- mcli/mygroup/__init__.py +3 -0
- mcli/public/__init__.py +1 -0
- mcli/public/commands/__init__.py +2 -0
- mcli/self/__init__.py +3 -0
- mcli/self/migrate_cmd.py +261 -0
- mcli/self/self_cmd.py +8 -0
- mcli/workflow/__init__.py +0 -0
- mcli/workflow/daemon/__init__.py +15 -0
- mcli/workflow/dashboard/__init__.py +5 -0
- mcli/workflow/docker/__init__.py +0 -0
- mcli/workflow/file/__init__.py +0 -0
- mcli/workflow/gcloud/__init__.py +1 -0
- mcli/workflow/git_commit/__init__.py +0 -0
- mcli/workflow/interview/__init__.py +0 -0
- mcli/workflow/notebook/__init__.py +16 -0
- mcli/workflow/registry/__init__.py +0 -0
- mcli/workflow/repo/__init__.py +0 -0
- mcli/workflow/scheduler/__init__.py +25 -0
- mcli/workflow/search/__init__.py +0 -0
- mcli/workflow/secrets/__init__.py +4 -0
- mcli/workflow/secrets/secrets_cmd.py +192 -0
- mcli/workflow/sync/__init__.py +5 -0
- mcli/workflow/videos/__init__.py +1 -0
- mcli/workflow/wakatime/__init__.py +80 -0
- mcli/workflow/workflow.py +22 -6
- {mcli_framework-7.10.2.dist-info → mcli_framework-7.11.0.dist-info}/METADATA +51 -54
- {mcli_framework-7.10.2.dist-info → mcli_framework-7.11.0.dist-info}/RECORD +80 -12
- {mcli_framework-7.10.2.dist-info → mcli_framework-7.11.0.dist-info}/WHEEL +0 -0
- {mcli_framework-7.10.2.dist-info → mcli_framework-7.11.0.dist-info}/entry_points.txt +0 -0
- {mcli_framework-7.10.2.dist-info → mcli_framework-7.11.0.dist-info}/licenses/LICENSE +0 -0
- {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
|
|
177
|
-
"""Manage
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
@
|
|
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
|
-
|
|
295
|
-
"""Search commands
|
|
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
|
-
@
|
|
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
|
-
@
|
|
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
|
-
@
|
|
703
|
+
@workflow.command("add")
|
|
680
704
|
@click.argument("command_name", required=True)
|
|
681
|
-
@click.option("--group",
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
842
|
-
|
|
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
|
-
@
|
|
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
|
-
|
|
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
|
|
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
|
-
@
|
|
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
|
-
|
|
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
|
|
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
|
-
@
|
|
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",
|
|
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
|
-
|
|
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
|
-
@
|
|
1146
|
-
|
|
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
|
-
@
|
|
1184
|
-
|
|
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
|
-
@
|
|
1259
|
+
@workflow.command("edit")
|
|
1199
1260
|
@click.argument("command_name")
|
|
1200
1261
|
@click.option("--editor", "-e", help="Editor to use (defaults to $EDITOR)")
|
|
1201
|
-
|
|
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
|
-
@
|
|
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
|
-
@
|
|
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
|
-
|
|
1470
|
-
"""Push
|
|
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
|
-
|
|
1525
|
-
"""Pull
|
|
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
|
-
|
|
1573
|
-
"""
|
|
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
|
-
@
|
|
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
|
)
|