mcli-framework 7.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mcli-framework might be problematic. Click here for more details.
- mcli/app/chat_cmd.py +42 -0
- mcli/app/commands_cmd.py +226 -0
- mcli/app/completion_cmd.py +216 -0
- mcli/app/completion_helpers.py +288 -0
- mcli/app/cron_test_cmd.py +697 -0
- mcli/app/logs_cmd.py +419 -0
- mcli/app/main.py +492 -0
- mcli/app/model/model.py +1060 -0
- mcli/app/model_cmd.py +227 -0
- mcli/app/redis_cmd.py +269 -0
- mcli/app/video/video.py +1114 -0
- mcli/app/visual_cmd.py +303 -0
- mcli/chat/chat.py +2409 -0
- mcli/chat/command_rag.py +514 -0
- mcli/chat/enhanced_chat.py +652 -0
- mcli/chat/system_controller.py +1010 -0
- mcli/chat/system_integration.py +1016 -0
- mcli/cli.py +25 -0
- mcli/config.toml +20 -0
- mcli/lib/api/api.py +586 -0
- mcli/lib/api/daemon_client.py +203 -0
- mcli/lib/api/daemon_client_local.py +44 -0
- mcli/lib/api/daemon_decorator.py +217 -0
- mcli/lib/api/mcli_decorators.py +1032 -0
- mcli/lib/auth/auth.py +85 -0
- mcli/lib/auth/aws_manager.py +85 -0
- mcli/lib/auth/azure_manager.py +91 -0
- mcli/lib/auth/credential_manager.py +192 -0
- mcli/lib/auth/gcp_manager.py +93 -0
- mcli/lib/auth/key_manager.py +117 -0
- mcli/lib/auth/mcli_manager.py +93 -0
- mcli/lib/auth/token_manager.py +75 -0
- mcli/lib/auth/token_util.py +1011 -0
- mcli/lib/config/config.py +47 -0
- mcli/lib/discovery/__init__.py +1 -0
- mcli/lib/discovery/command_discovery.py +274 -0
- mcli/lib/erd/erd.py +1345 -0
- mcli/lib/erd/generate_graph.py +453 -0
- mcli/lib/files/files.py +76 -0
- mcli/lib/fs/fs.py +109 -0
- mcli/lib/lib.py +29 -0
- mcli/lib/logger/logger.py +611 -0
- mcli/lib/performance/optimizer.py +409 -0
- mcli/lib/performance/rust_bridge.py +502 -0
- mcli/lib/performance/uvloop_config.py +154 -0
- mcli/lib/pickles/pickles.py +50 -0
- mcli/lib/search/cached_vectorizer.py +479 -0
- mcli/lib/services/data_pipeline.py +460 -0
- mcli/lib/services/lsh_client.py +441 -0
- mcli/lib/services/redis_service.py +387 -0
- mcli/lib/shell/shell.py +137 -0
- mcli/lib/toml/toml.py +33 -0
- mcli/lib/ui/styling.py +47 -0
- mcli/lib/ui/visual_effects.py +634 -0
- mcli/lib/watcher/watcher.py +185 -0
- mcli/ml/api/app.py +215 -0
- mcli/ml/api/middleware.py +224 -0
- mcli/ml/api/routers/admin_router.py +12 -0
- mcli/ml/api/routers/auth_router.py +244 -0
- mcli/ml/api/routers/backtest_router.py +12 -0
- mcli/ml/api/routers/data_router.py +12 -0
- mcli/ml/api/routers/model_router.py +302 -0
- mcli/ml/api/routers/monitoring_router.py +12 -0
- mcli/ml/api/routers/portfolio_router.py +12 -0
- mcli/ml/api/routers/prediction_router.py +267 -0
- mcli/ml/api/routers/trade_router.py +12 -0
- mcli/ml/api/routers/websocket_router.py +76 -0
- mcli/ml/api/schemas.py +64 -0
- mcli/ml/auth/auth_manager.py +425 -0
- mcli/ml/auth/models.py +154 -0
- mcli/ml/auth/permissions.py +302 -0
- mcli/ml/backtesting/backtest_engine.py +502 -0
- mcli/ml/backtesting/performance_metrics.py +393 -0
- mcli/ml/cache.py +400 -0
- mcli/ml/cli/main.py +398 -0
- mcli/ml/config/settings.py +394 -0
- mcli/ml/configs/dvc_config.py +230 -0
- mcli/ml/configs/mlflow_config.py +131 -0
- mcli/ml/configs/mlops_manager.py +293 -0
- mcli/ml/dashboard/app.py +532 -0
- mcli/ml/dashboard/app_integrated.py +738 -0
- mcli/ml/dashboard/app_supabase.py +560 -0
- mcli/ml/dashboard/app_training.py +615 -0
- mcli/ml/dashboard/cli.py +51 -0
- mcli/ml/data_ingestion/api_connectors.py +501 -0
- mcli/ml/data_ingestion/data_pipeline.py +567 -0
- mcli/ml/data_ingestion/stream_processor.py +512 -0
- mcli/ml/database/migrations/env.py +94 -0
- mcli/ml/database/models.py +667 -0
- mcli/ml/database/session.py +200 -0
- mcli/ml/experimentation/ab_testing.py +845 -0
- mcli/ml/features/ensemble_features.py +607 -0
- mcli/ml/features/political_features.py +676 -0
- mcli/ml/features/recommendation_engine.py +809 -0
- mcli/ml/features/stock_features.py +573 -0
- mcli/ml/features/test_feature_engineering.py +346 -0
- mcli/ml/logging.py +85 -0
- mcli/ml/mlops/data_versioning.py +518 -0
- mcli/ml/mlops/experiment_tracker.py +377 -0
- mcli/ml/mlops/model_serving.py +481 -0
- mcli/ml/mlops/pipeline_orchestrator.py +614 -0
- mcli/ml/models/base_models.py +324 -0
- mcli/ml/models/ensemble_models.py +675 -0
- mcli/ml/models/recommendation_models.py +474 -0
- mcli/ml/models/test_models.py +487 -0
- mcli/ml/monitoring/drift_detection.py +676 -0
- mcli/ml/monitoring/metrics.py +45 -0
- mcli/ml/optimization/portfolio_optimizer.py +834 -0
- mcli/ml/preprocessing/data_cleaners.py +451 -0
- mcli/ml/preprocessing/feature_extractors.py +491 -0
- mcli/ml/preprocessing/ml_pipeline.py +382 -0
- mcli/ml/preprocessing/politician_trading_preprocessor.py +569 -0
- mcli/ml/preprocessing/test_preprocessing.py +294 -0
- mcli/ml/scripts/populate_sample_data.py +200 -0
- mcli/ml/tasks.py +400 -0
- mcli/ml/tests/test_integration.py +429 -0
- mcli/ml/tests/test_training_dashboard.py +387 -0
- mcli/public/oi/oi.py +15 -0
- mcli/public/public.py +4 -0
- mcli/self/self_cmd.py +1246 -0
- mcli/workflow/daemon/api_daemon.py +800 -0
- mcli/workflow/daemon/async_command_database.py +681 -0
- mcli/workflow/daemon/async_process_manager.py +591 -0
- mcli/workflow/daemon/client.py +530 -0
- mcli/workflow/daemon/commands.py +1196 -0
- mcli/workflow/daemon/daemon.py +905 -0
- mcli/workflow/daemon/daemon_api.py +59 -0
- mcli/workflow/daemon/enhanced_daemon.py +571 -0
- mcli/workflow/daemon/process_cli.py +244 -0
- mcli/workflow/daemon/process_manager.py +439 -0
- mcli/workflow/daemon/test_daemon.py +275 -0
- mcli/workflow/dashboard/dashboard_cmd.py +113 -0
- mcli/workflow/docker/docker.py +0 -0
- mcli/workflow/file/file.py +100 -0
- mcli/workflow/gcloud/config.toml +21 -0
- mcli/workflow/gcloud/gcloud.py +58 -0
- mcli/workflow/git_commit/ai_service.py +328 -0
- mcli/workflow/git_commit/commands.py +430 -0
- mcli/workflow/lsh_integration.py +355 -0
- mcli/workflow/model_service/client.py +594 -0
- mcli/workflow/model_service/download_and_run_efficient_models.py +288 -0
- mcli/workflow/model_service/lightweight_embedder.py +397 -0
- mcli/workflow/model_service/lightweight_model_server.py +714 -0
- mcli/workflow/model_service/lightweight_test.py +241 -0
- mcli/workflow/model_service/model_service.py +1955 -0
- mcli/workflow/model_service/ollama_efficient_runner.py +425 -0
- mcli/workflow/model_service/pdf_processor.py +386 -0
- mcli/workflow/model_service/test_efficient_runner.py +234 -0
- mcli/workflow/model_service/test_example.py +315 -0
- mcli/workflow/model_service/test_integration.py +131 -0
- mcli/workflow/model_service/test_new_features.py +149 -0
- mcli/workflow/openai/openai.py +99 -0
- mcli/workflow/politician_trading/commands.py +1790 -0
- mcli/workflow/politician_trading/config.py +134 -0
- mcli/workflow/politician_trading/connectivity.py +490 -0
- mcli/workflow/politician_trading/data_sources.py +395 -0
- mcli/workflow/politician_trading/database.py +410 -0
- mcli/workflow/politician_trading/demo.py +248 -0
- mcli/workflow/politician_trading/models.py +165 -0
- mcli/workflow/politician_trading/monitoring.py +413 -0
- mcli/workflow/politician_trading/scrapers.py +966 -0
- mcli/workflow/politician_trading/scrapers_california.py +412 -0
- mcli/workflow/politician_trading/scrapers_eu.py +377 -0
- mcli/workflow/politician_trading/scrapers_uk.py +350 -0
- mcli/workflow/politician_trading/scrapers_us_states.py +438 -0
- mcli/workflow/politician_trading/supabase_functions.py +354 -0
- mcli/workflow/politician_trading/workflow.py +852 -0
- mcli/workflow/registry/registry.py +180 -0
- mcli/workflow/repo/repo.py +223 -0
- mcli/workflow/scheduler/commands.py +493 -0
- mcli/workflow/scheduler/cron_parser.py +238 -0
- mcli/workflow/scheduler/job.py +182 -0
- mcli/workflow/scheduler/monitor.py +139 -0
- mcli/workflow/scheduler/persistence.py +324 -0
- mcli/workflow/scheduler/scheduler.py +679 -0
- mcli/workflow/sync/sync_cmd.py +437 -0
- mcli/workflow/sync/test_cmd.py +314 -0
- mcli/workflow/videos/videos.py +242 -0
- mcli/workflow/wakatime/wakatime.py +11 -0
- mcli/workflow/workflow.py +37 -0
- mcli_framework-7.0.0.dist-info/METADATA +479 -0
- mcli_framework-7.0.0.dist-info/RECORD +186 -0
- mcli_framework-7.0.0.dist-info/WHEEL +5 -0
- mcli_framework-7.0.0.dist-info/entry_points.txt +7 -0
- mcli_framework-7.0.0.dist-info/licenses/LICENSE +21 -0
- mcli_framework-7.0.0.dist-info/top_level.txt +1 -0
mcli/app/chat_cmd.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from mcli.chat.chat import ChatClient
|
|
4
|
+
from mcli.chat.enhanced_chat import EnhancedChatClient
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@click.command()
|
|
8
|
+
@click.option(
|
|
9
|
+
"--remote", is_flag=True, help="Use remote online models instead of local lightweight models"
|
|
10
|
+
)
|
|
11
|
+
@click.option("--model", "-m", help="Specific model to use (overrides default behavior)")
|
|
12
|
+
@click.option(
|
|
13
|
+
"--enhanced",
|
|
14
|
+
is_flag=True,
|
|
15
|
+
default=True,
|
|
16
|
+
help="Use enhanced chat with RAG-based command search (default: enabled)",
|
|
17
|
+
)
|
|
18
|
+
@click.option("--classic", is_flag=True, help="Use classic chat interface without command search")
|
|
19
|
+
def chat(remote: bool, model: str, enhanced: bool, classic: bool):
|
|
20
|
+
"""Start an interactive chat session with the MCLI Chat Assistant.
|
|
21
|
+
|
|
22
|
+
🤖 Enhanced Mode (Default):
|
|
23
|
+
- Self-referential command discovery and suggestions
|
|
24
|
+
- RAG-based semantic search of available MCLI commands
|
|
25
|
+
- Intelligent intent analysis and contextual recommendations
|
|
26
|
+
- Real-time system status awareness
|
|
27
|
+
|
|
28
|
+
💬 Classic Mode:
|
|
29
|
+
- Traditional chat interface
|
|
30
|
+
- Basic system integration
|
|
31
|
+
|
|
32
|
+
By default, uses lightweight local models for privacy and speed.
|
|
33
|
+
Use --remote to connect to online models like OpenAI or Anthropic.
|
|
34
|
+
"""
|
|
35
|
+
# Choose chat client based on options
|
|
36
|
+
if classic:
|
|
37
|
+
client = ChatClient(use_remote=remote, model_override=model)
|
|
38
|
+
client.start_interactive_session()
|
|
39
|
+
else:
|
|
40
|
+
# Use enhanced client by default
|
|
41
|
+
client = EnhancedChatClient(use_remote=remote, model_override=model)
|
|
42
|
+
client.start_interactive_session()
|
mcli/app/commands_cmd.py
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
import click
|
|
5
|
+
|
|
6
|
+
from mcli.lib.api.daemon_client import get_daemon_client
|
|
7
|
+
from mcli.lib.discovery.command_discovery import get_command_discovery
|
|
8
|
+
from mcli.lib.ui.styling import console
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@click.group()
|
|
12
|
+
def commands():
|
|
13
|
+
"""Manage and execute available commands."""
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@commands.command("list")
|
|
18
|
+
@click.option("--include-groups", is_flag=True, help="Include command groups in listing")
|
|
19
|
+
@click.option("--daemon-only", is_flag=True, help="Show only daemon database commands")
|
|
20
|
+
@click.option("--json", "as_json", is_flag=True, help="Output as JSON")
|
|
21
|
+
def list_commands(include_groups: bool, daemon_only: bool, as_json: bool):
|
|
22
|
+
"""List all available commands"""
|
|
23
|
+
try:
|
|
24
|
+
if daemon_only:
|
|
25
|
+
# Show only daemon database commands
|
|
26
|
+
client = get_daemon_client()
|
|
27
|
+
result = client.list_commands(all=True)
|
|
28
|
+
|
|
29
|
+
if isinstance(result, dict):
|
|
30
|
+
commands_data = result.get("commands", [])
|
|
31
|
+
elif isinstance(result, list):
|
|
32
|
+
commands_data = result
|
|
33
|
+
else:
|
|
34
|
+
commands_data = []
|
|
35
|
+
else:
|
|
36
|
+
# Show all discovered Click commands
|
|
37
|
+
discovery = get_command_discovery()
|
|
38
|
+
commands_data = discovery.get_commands(include_groups=include_groups)
|
|
39
|
+
|
|
40
|
+
if as_json:
|
|
41
|
+
click.echo(
|
|
42
|
+
json.dumps({"commands": commands_data, "total": len(commands_data)}, indent=2)
|
|
43
|
+
)
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
if not commands_data:
|
|
47
|
+
console.print("No commands found")
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
console.print(f"[bold]Available Commands ({len(commands_data)}):[/bold]")
|
|
51
|
+
for cmd in commands_data:
|
|
52
|
+
# Handle different command sources
|
|
53
|
+
if daemon_only:
|
|
54
|
+
status = "[red][INACTIVE][/red] " if not cmd.get("is_active", True) else ""
|
|
55
|
+
console.print(
|
|
56
|
+
f"{status}• [green]{cmd['name']}[/green] ({cmd.get('language', 'python')})"
|
|
57
|
+
)
|
|
58
|
+
else:
|
|
59
|
+
group_indicator = "[blue][GROUP][/blue] " if cmd.get("is_group") else ""
|
|
60
|
+
console.print(f"{group_indicator}• [green]{cmd['full_name']}[/green]")
|
|
61
|
+
|
|
62
|
+
if cmd.get("description"):
|
|
63
|
+
console.print(f" {cmd['description']}")
|
|
64
|
+
if cmd.get("module"):
|
|
65
|
+
console.print(f" Module: {cmd['module']}")
|
|
66
|
+
if cmd.get("tags"):
|
|
67
|
+
console.print(f" Tags: {', '.join(cmd['tags'])}")
|
|
68
|
+
console.print()
|
|
69
|
+
|
|
70
|
+
except Exception as e:
|
|
71
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@commands.command("search")
|
|
75
|
+
@click.argument("query")
|
|
76
|
+
@click.option("--daemon-only", is_flag=True, help="Search only daemon database commands")
|
|
77
|
+
@click.option("--json", "as_json", is_flag=True, help="Output as JSON")
|
|
78
|
+
def search_commands(query: str, daemon_only: bool, as_json: bool):
|
|
79
|
+
"""Search commands by name, description, or tags"""
|
|
80
|
+
try:
|
|
81
|
+
if daemon_only:
|
|
82
|
+
# Search only daemon database commands
|
|
83
|
+
client = get_daemon_client()
|
|
84
|
+
result = client.list_commands(all=True)
|
|
85
|
+
|
|
86
|
+
if isinstance(result, dict):
|
|
87
|
+
all_commands = result.get("commands", [])
|
|
88
|
+
elif isinstance(result, list):
|
|
89
|
+
all_commands = result
|
|
90
|
+
else:
|
|
91
|
+
all_commands = []
|
|
92
|
+
|
|
93
|
+
# Filter commands that match the query
|
|
94
|
+
matching_commands = [
|
|
95
|
+
cmd
|
|
96
|
+
for cmd in all_commands
|
|
97
|
+
if (
|
|
98
|
+
query.lower() in cmd["name"].lower()
|
|
99
|
+
or query.lower() in (cmd["description"] or "").lower()
|
|
100
|
+
or any(query.lower() in tag.lower() for tag in cmd.get("tags", []))
|
|
101
|
+
)
|
|
102
|
+
]
|
|
103
|
+
else:
|
|
104
|
+
# Search all discovered Click commands
|
|
105
|
+
discovery = get_command_discovery()
|
|
106
|
+
matching_commands = discovery.search_commands(query)
|
|
107
|
+
|
|
108
|
+
if as_json:
|
|
109
|
+
click.echo(
|
|
110
|
+
json.dumps(
|
|
111
|
+
{
|
|
112
|
+
"commands": matching_commands,
|
|
113
|
+
"total": len(matching_commands),
|
|
114
|
+
"query": query,
|
|
115
|
+
},
|
|
116
|
+
indent=2,
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
return
|
|
120
|
+
|
|
121
|
+
if not matching_commands:
|
|
122
|
+
console.print(f"No commands found matching '[yellow]{query}[/yellow]'")
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
console.print(f"[bold]Commands matching '{query}' ({len(matching_commands)}):[/bold]")
|
|
126
|
+
for cmd in matching_commands:
|
|
127
|
+
if daemon_only:
|
|
128
|
+
status = "[red][INACTIVE][/red] " if not cmd.get("is_active", True) else ""
|
|
129
|
+
console.print(
|
|
130
|
+
f"{status}• [green]{cmd['name']}[/green] ({cmd.get('language', 'python')})"
|
|
131
|
+
)
|
|
132
|
+
else:
|
|
133
|
+
group_indicator = "[blue][GROUP][/blue] " if cmd.get("is_group") else ""
|
|
134
|
+
console.print(f"{group_indicator}• [green]{cmd['full_name']}[/green]")
|
|
135
|
+
|
|
136
|
+
console.print(f" [italic]{cmd['description']}[/italic]")
|
|
137
|
+
if cmd.get("module"):
|
|
138
|
+
console.print(f" Module: {cmd['module']}")
|
|
139
|
+
console.print()
|
|
140
|
+
|
|
141
|
+
except Exception as e:
|
|
142
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@commands.command("execute")
|
|
146
|
+
@click.argument("command_name")
|
|
147
|
+
@click.argument("args", nargs=-1)
|
|
148
|
+
@click.option("--json", "as_json", is_flag=True, help="Output as JSON")
|
|
149
|
+
@click.option("--timeout", type=int, help="Execution timeout in seconds")
|
|
150
|
+
def execute_command(command_name: str, args: tuple, as_json: bool, timeout: Optional[int]):
|
|
151
|
+
"""Execute a command by name"""
|
|
152
|
+
try:
|
|
153
|
+
client = get_daemon_client()
|
|
154
|
+
result = client.execute_command(command_name=command_name, args=list(args), timeout=timeout)
|
|
155
|
+
|
|
156
|
+
if as_json:
|
|
157
|
+
click.echo(json.dumps(result, indent=2))
|
|
158
|
+
return
|
|
159
|
+
|
|
160
|
+
if result.get("success"):
|
|
161
|
+
if result.get("output"):
|
|
162
|
+
console.print(f"[green]Output:[/green]\n{result['output']}")
|
|
163
|
+
else:
|
|
164
|
+
console.print("[green]Command executed successfully[/green]")
|
|
165
|
+
|
|
166
|
+
if result.get("execution_time_ms"):
|
|
167
|
+
console.print(f"[dim]Execution time: {result['execution_time_ms']}ms[/dim]")
|
|
168
|
+
else:
|
|
169
|
+
console.print(f"[red]Error:[/red] {result.get('error', 'Unknown error')}")
|
|
170
|
+
|
|
171
|
+
except Exception as e:
|
|
172
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
@commands.command("info")
|
|
176
|
+
@click.argument("command_name")
|
|
177
|
+
@click.option("--json", "as_json", is_flag=True, help="Output as JSON")
|
|
178
|
+
def command_info(command_name: str, as_json: bool):
|
|
179
|
+
"""Show detailed information about a command"""
|
|
180
|
+
try:
|
|
181
|
+
client = get_daemon_client()
|
|
182
|
+
result = client.list_commands(all=True)
|
|
183
|
+
|
|
184
|
+
if isinstance(result, dict):
|
|
185
|
+
all_commands = result.get("commands", [])
|
|
186
|
+
elif isinstance(result, list):
|
|
187
|
+
all_commands = result
|
|
188
|
+
else:
|
|
189
|
+
all_commands = []
|
|
190
|
+
|
|
191
|
+
# Find the command
|
|
192
|
+
command = None
|
|
193
|
+
for cmd in all_commands:
|
|
194
|
+
if cmd["name"].lower() == command_name.lower():
|
|
195
|
+
command = cmd
|
|
196
|
+
break
|
|
197
|
+
|
|
198
|
+
if not command:
|
|
199
|
+
console.print(f"[red]Command '{command_name}' not found[/red]")
|
|
200
|
+
return
|
|
201
|
+
|
|
202
|
+
if as_json:
|
|
203
|
+
click.echo(json.dumps(command, indent=2))
|
|
204
|
+
return
|
|
205
|
+
|
|
206
|
+
console.print(f"[bold]Command: {command['name']}[/bold]")
|
|
207
|
+
console.print(f"Language: {command['language']}")
|
|
208
|
+
console.print(f"Description: {command.get('description', 'No description')}")
|
|
209
|
+
console.print(f"Group: {command.get('group', 'None')}")
|
|
210
|
+
console.print(f"Tags: {', '.join(command.get('tags', []))}")
|
|
211
|
+
console.print(f"Active: {'Yes' if command.get('is_active', True) else 'No'}")
|
|
212
|
+
console.print(f"Execution Count: {command.get('execution_count', 0)}")
|
|
213
|
+
|
|
214
|
+
if command.get("created_at"):
|
|
215
|
+
console.print(f"Created: {command['created_at']}")
|
|
216
|
+
if command.get("last_executed"):
|
|
217
|
+
console.print(f"Last Executed: {command['last_executed']}")
|
|
218
|
+
|
|
219
|
+
if command.get("code"):
|
|
220
|
+
console.print(f"\n[bold]Code:[/bold]")
|
|
221
|
+
console.print(f"```{command['language']}")
|
|
222
|
+
console.print(command["code"])
|
|
223
|
+
console.print("```")
|
|
224
|
+
|
|
225
|
+
except Exception as e:
|
|
226
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Shell completion commands for MCLI
|
|
3
|
+
|
|
4
|
+
Provides commands to generate and install shell completion scripts
|
|
5
|
+
for bash, zsh, and fish shells.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import click
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from mcli.lib.ui.styling import success
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@click.group(name="completion")
|
|
15
|
+
def completion():
|
|
16
|
+
"""Shell completion utilities"""
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@completion.command(name="bash")
|
|
21
|
+
@click.pass_context
|
|
22
|
+
def bash_completion(ctx):
|
|
23
|
+
"""Generate bash completion script"""
|
|
24
|
+
from click.shell_completion import BashComplete
|
|
25
|
+
|
|
26
|
+
# Get the root CLI app
|
|
27
|
+
app = ctx.find_root().command
|
|
28
|
+
complete = BashComplete(app, {}, "mcli", "complete")
|
|
29
|
+
script = complete.source()
|
|
30
|
+
|
|
31
|
+
click.echo("# Bash completion script for MCLI")
|
|
32
|
+
click.echo("# Add this to your ~/.bashrc or ~/.bash_profile:")
|
|
33
|
+
click.echo()
|
|
34
|
+
click.echo(script)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@completion.command(name="zsh")
|
|
38
|
+
@click.pass_context
|
|
39
|
+
def zsh_completion(ctx):
|
|
40
|
+
"""Generate zsh completion script"""
|
|
41
|
+
from click.shell_completion import ZshComplete
|
|
42
|
+
|
|
43
|
+
# Get the root CLI app
|
|
44
|
+
app = ctx.find_root().command
|
|
45
|
+
complete = ZshComplete(app, {}, "mcli", "complete")
|
|
46
|
+
script = complete.source()
|
|
47
|
+
|
|
48
|
+
click.echo("# Zsh completion script for MCLI")
|
|
49
|
+
click.echo("# Add this to your ~/.zshrc:")
|
|
50
|
+
click.echo()
|
|
51
|
+
click.echo(script)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@completion.command(name="fish")
|
|
55
|
+
@click.pass_context
|
|
56
|
+
def fish_completion(ctx):
|
|
57
|
+
"""Generate fish completion script"""
|
|
58
|
+
from click.shell_completion import FishComplete
|
|
59
|
+
|
|
60
|
+
# Get the root CLI app
|
|
61
|
+
app = ctx.find_root().command
|
|
62
|
+
complete = FishComplete(app, {}, "mcli", "complete")
|
|
63
|
+
script = complete.source()
|
|
64
|
+
|
|
65
|
+
click.echo("# Fish completion script for MCLI")
|
|
66
|
+
click.echo("# Add this to ~/.config/fish/completions/mcli.fish:")
|
|
67
|
+
click.echo()
|
|
68
|
+
click.echo(script)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@completion.command(name="install")
|
|
72
|
+
@click.option("--shell", type=click.Choice(['bash', 'zsh', 'fish']),
|
|
73
|
+
help="Shell to install for (auto-detected if not specified)")
|
|
74
|
+
@click.pass_context
|
|
75
|
+
def install_completion(ctx, shell):
|
|
76
|
+
"""Install shell completion for the current user"""
|
|
77
|
+
import subprocess
|
|
78
|
+
|
|
79
|
+
# Auto-detect shell if not specified
|
|
80
|
+
if not shell:
|
|
81
|
+
shell_path = os.environ.get('SHELL', '')
|
|
82
|
+
if 'bash' in shell_path:
|
|
83
|
+
shell = 'bash'
|
|
84
|
+
elif 'zsh' in shell_path:
|
|
85
|
+
shell = 'zsh'
|
|
86
|
+
elif 'fish' in shell_path:
|
|
87
|
+
shell = 'fish'
|
|
88
|
+
else:
|
|
89
|
+
click.echo("❌ Could not auto-detect shell. Please specify --shell")
|
|
90
|
+
return
|
|
91
|
+
|
|
92
|
+
# Get the root CLI app
|
|
93
|
+
app = ctx.find_root().command
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
if shell == 'bash':
|
|
97
|
+
from click.shell_completion import BashComplete
|
|
98
|
+
complete = BashComplete(app, {}, "mcli", "complete")
|
|
99
|
+
script = complete.source()
|
|
100
|
+
|
|
101
|
+
# Install to bash completion directory
|
|
102
|
+
bash_completion_dir = Path.home() / ".bash_completion.d"
|
|
103
|
+
bash_completion_dir.mkdir(exist_ok=True)
|
|
104
|
+
completion_file = bash_completion_dir / "mcli"
|
|
105
|
+
completion_file.write_text(script)
|
|
106
|
+
|
|
107
|
+
# Add sourcing to .bashrc if needed
|
|
108
|
+
bashrc = Path.home() / ".bashrc"
|
|
109
|
+
source_line = f"[ -f {completion_file} ] && source {completion_file}"
|
|
110
|
+
|
|
111
|
+
if bashrc.exists():
|
|
112
|
+
content = bashrc.read_text()
|
|
113
|
+
if source_line not in content:
|
|
114
|
+
with bashrc.open("a") as f:
|
|
115
|
+
f.write(f"\n# MCLI completion\n{source_line}\n")
|
|
116
|
+
click.echo("✅ Added completion sourcing to ~/.bashrc")
|
|
117
|
+
else:
|
|
118
|
+
click.echo("ℹ️ Completion already configured in ~/.bashrc")
|
|
119
|
+
else:
|
|
120
|
+
click.echo(f"✅ Completion installed to {completion_file}")
|
|
121
|
+
click.echo("💡 Add this to your ~/.bashrc:")
|
|
122
|
+
click.echo(source_line)
|
|
123
|
+
|
|
124
|
+
elif shell == 'zsh':
|
|
125
|
+
from click.shell_completion import ZshComplete
|
|
126
|
+
complete = ZshComplete(app, {}, "mcli", "complete")
|
|
127
|
+
script = complete.source()
|
|
128
|
+
|
|
129
|
+
# Install to zsh completion directory
|
|
130
|
+
zsh_completion_dir = Path.home() / ".config" / "zsh" / "completions"
|
|
131
|
+
zsh_completion_dir.mkdir(parents=True, exist_ok=True)
|
|
132
|
+
completion_file = zsh_completion_dir / "_mcli"
|
|
133
|
+
completion_file.write_text(script)
|
|
134
|
+
|
|
135
|
+
# Add to fpath in .zshrc if needed
|
|
136
|
+
zshrc = Path.home() / ".zshrc"
|
|
137
|
+
fpath_line = f'fpath=("{zsh_completion_dir}" $fpath)'
|
|
138
|
+
|
|
139
|
+
if zshrc.exists():
|
|
140
|
+
content = zshrc.read_text()
|
|
141
|
+
if str(zsh_completion_dir) not in content:
|
|
142
|
+
with zshrc.open("a") as f:
|
|
143
|
+
f.write(f"\n# MCLI completion\n{fpath_line}\nautoload -U compinit && compinit\n")
|
|
144
|
+
click.echo("✅ Added completion to ~/.zshrc")
|
|
145
|
+
else:
|
|
146
|
+
click.echo("ℹ️ Completion already configured in ~/.zshrc")
|
|
147
|
+
else:
|
|
148
|
+
click.echo(f"✅ Completion installed to {completion_file}")
|
|
149
|
+
click.echo("💡 Add this to your ~/.zshrc:")
|
|
150
|
+
click.echo(f"{fpath_line}\nautoload -U compinit && compinit")
|
|
151
|
+
|
|
152
|
+
elif shell == 'fish':
|
|
153
|
+
from click.shell_completion import FishComplete
|
|
154
|
+
complete = FishComplete(app, {}, "mcli", "complete")
|
|
155
|
+
script = complete.source()
|
|
156
|
+
|
|
157
|
+
# Install to fish completion directory
|
|
158
|
+
fish_completion_dir = Path.home() / ".config" / "fish" / "completions"
|
|
159
|
+
fish_completion_dir.mkdir(parents=True, exist_ok=True)
|
|
160
|
+
completion_file = fish_completion_dir / "mcli.fish"
|
|
161
|
+
completion_file.write_text(script)
|
|
162
|
+
click.echo(f"✅ Completion installed to {completion_file}")
|
|
163
|
+
|
|
164
|
+
click.echo(f"🎉 Shell completion for {shell} installed successfully!")
|
|
165
|
+
click.echo("💡 Restart your shell or source your profile to enable completions")
|
|
166
|
+
|
|
167
|
+
except Exception as e:
|
|
168
|
+
click.echo(f"❌ Failed to install completion: {e}")
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@completion.command(name="status")
|
|
172
|
+
def completion_status():
|
|
173
|
+
"""Check current shell completion status"""
|
|
174
|
+
current_shell = os.environ.get('SHELL', 'unknown')
|
|
175
|
+
shell_name = Path(current_shell).name if current_shell != 'unknown' else 'unknown'
|
|
176
|
+
|
|
177
|
+
click.echo(f"🐚 Current shell: {shell_name} ({current_shell})")
|
|
178
|
+
click.echo()
|
|
179
|
+
|
|
180
|
+
# Check for existing completions
|
|
181
|
+
completions_found = []
|
|
182
|
+
|
|
183
|
+
# Check bash
|
|
184
|
+
bash_completion = Path.home() / ".bash_completion.d" / "mcli"
|
|
185
|
+
if bash_completion.exists():
|
|
186
|
+
completions_found.append(f"✅ Bash completion: {bash_completion}")
|
|
187
|
+
else:
|
|
188
|
+
completions_found.append("❌ Bash completion: Not installed")
|
|
189
|
+
|
|
190
|
+
# Check zsh
|
|
191
|
+
zsh_completion = Path.home() / ".config" / "zsh" / "completions" / "_mcli"
|
|
192
|
+
if zsh_completion.exists():
|
|
193
|
+
completions_found.append(f"✅ Zsh completion: {zsh_completion}")
|
|
194
|
+
else:
|
|
195
|
+
completions_found.append("❌ Zsh completion: Not installed")
|
|
196
|
+
|
|
197
|
+
# Check fish
|
|
198
|
+
fish_completion = Path.home() / ".config" / "fish" / "completions" / "mcli.fish"
|
|
199
|
+
if fish_completion.exists():
|
|
200
|
+
completions_found.append(f"✅ Fish completion: {fish_completion}")
|
|
201
|
+
else:
|
|
202
|
+
completions_found.append("❌ Fish completion: Not installed")
|
|
203
|
+
|
|
204
|
+
for status in completions_found:
|
|
205
|
+
click.echo(status)
|
|
206
|
+
|
|
207
|
+
click.echo()
|
|
208
|
+
click.echo("💡 To install completion for your shell:")
|
|
209
|
+
click.echo(" mcli completion install")
|
|
210
|
+
click.echo()
|
|
211
|
+
click.echo("💡 To generate completion script manually:")
|
|
212
|
+
click.echo(f" mcli completion {shell_name}")
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
# Export the CLI group for registration
|
|
216
|
+
cli = completion
|