mcli-framework 7.10.1__py3-none-any.whl → 7.10.2__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/lib/custom_commands.py +10 -0
- mcli/lib/optional_deps.py +240 -0
- mcli/workflow/git_commit/ai_service.py +13 -2
- mcli/workflow/notebook/converter.py +375 -0
- mcli/workflow/notebook/notebook_cmd.py +441 -0
- mcli/workflow/notebook/schema.py +402 -0
- mcli/workflow/notebook/validator.py +313 -0
- mcli/workflow/workflow.py +14 -0
- {mcli_framework-7.10.1.dist-info → mcli_framework-7.10.2.dist-info}/METADATA +36 -2
- {mcli_framework-7.10.1.dist-info → mcli_framework-7.10.2.dist-info}/RECORD +14 -94
- mcli/__init__.py +0 -160
- mcli/__main__.py +0 -14
- mcli/app/__init__.py +0 -23
- mcli/app/model/__init__.py +0 -0
- mcli/app/video/__init__.py +0 -5
- mcli/chat/__init__.py +0 -34
- mcli/lib/__init__.py +0 -0
- mcli/lib/api/__init__.py +0 -0
- mcli/lib/auth/__init__.py +0 -1
- mcli/lib/config/__init__.py +0 -1
- mcli/lib/erd/__init__.py +0 -25
- mcli/lib/files/__init__.py +0 -0
- mcli/lib/fs/__init__.py +0 -1
- mcli/lib/logger/__init__.py +0 -3
- mcli/lib/performance/__init__.py +0 -17
- mcli/lib/pickles/__init__.py +0 -1
- mcli/lib/secrets/__init__.py +0 -10
- mcli/lib/shell/__init__.py +0 -0
- mcli/lib/toml/__init__.py +0 -1
- mcli/lib/watcher/__init__.py +0 -0
- mcli/ml/__init__.py +0 -16
- mcli/ml/api/__init__.py +0 -30
- mcli/ml/api/routers/__init__.py +0 -27
- mcli/ml/auth/__init__.py +0 -41
- mcli/ml/backtesting/__init__.py +0 -33
- mcli/ml/cli/__init__.py +0 -5
- mcli/ml/config/__init__.py +0 -33
- mcli/ml/configs/__init__.py +0 -16
- mcli/ml/dashboard/__init__.py +0 -12
- mcli/ml/dashboard/components/__init__.py +0 -7
- mcli/ml/dashboard/pages/__init__.py +0 -6
- mcli/ml/data_ingestion/__init__.py +0 -29
- mcli/ml/database/__init__.py +0 -40
- mcli/ml/experimentation/__init__.py +0 -29
- mcli/ml/features/__init__.py +0 -39
- mcli/ml/features/political_features.py +0 -677
- mcli/ml/mlops/__init__.py +0 -19
- mcli/ml/models/__init__.py +0 -90
- mcli/ml/monitoring/__init__.py +0 -25
- mcli/ml/optimization/__init__.py +0 -27
- mcli/ml/predictions/__init__.py +0 -5
- mcli/ml/preprocessing/__init__.py +0 -24
- mcli/ml/preprocessing/politician_trading_preprocessor.py +0 -570
- mcli/ml/scripts/__init__.py +0 -1
- mcli/ml/serving/__init__.py +0 -1
- mcli/ml/trading/__init__.py +0 -63
- mcli/ml/training/__init__.py +0 -7
- mcli/mygroup/__init__.py +0 -3
- mcli/public/__init__.py +0 -1
- mcli/public/commands/__init__.py +0 -2
- mcli/self/__init__.py +0 -3
- mcli/workflow/__init__.py +0 -0
- mcli/workflow/daemon/__init__.py +0 -15
- mcli/workflow/dashboard/__init__.py +0 -5
- mcli/workflow/docker/__init__.py +0 -0
- mcli/workflow/file/__init__.py +0 -0
- mcli/workflow/gcloud/__init__.py +0 -1
- mcli/workflow/git_commit/__init__.py +0 -0
- mcli/workflow/interview/__init__.py +0 -0
- mcli/workflow/politician_trading/__init__.py +0 -4
- mcli/workflow/politician_trading/config.py +0 -134
- mcli/workflow/politician_trading/connectivity.py +0 -492
- mcli/workflow/politician_trading/data_sources.py +0 -654
- mcli/workflow/politician_trading/database.py +0 -412
- mcli/workflow/politician_trading/demo.py +0 -249
- mcli/workflow/politician_trading/models.py +0 -327
- mcli/workflow/politician_trading/monitoring.py +0 -413
- mcli/workflow/politician_trading/scrapers.py +0 -1074
- mcli/workflow/politician_trading/scrapers_california.py +0 -434
- mcli/workflow/politician_trading/scrapers_corporate_registry.py +0 -797
- mcli/workflow/politician_trading/scrapers_eu.py +0 -376
- mcli/workflow/politician_trading/scrapers_free_sources.py +0 -509
- mcli/workflow/politician_trading/scrapers_third_party.py +0 -373
- mcli/workflow/politician_trading/scrapers_uk.py +0 -378
- mcli/workflow/politician_trading/scrapers_us_states.py +0 -471
- mcli/workflow/politician_trading/seed_database.py +0 -520
- mcli/workflow/politician_trading/supabase_functions.py +0 -354
- mcli/workflow/politician_trading/workflow.py +0 -879
- mcli/workflow/registry/__init__.py +0 -0
- mcli/workflow/repo/__init__.py +0 -0
- mcli/workflow/scheduler/__init__.py +0 -25
- mcli/workflow/search/__init__.py +0 -0
- mcli/workflow/sync/__init__.py +0 -5
- mcli/workflow/videos/__init__.py +0 -1
- mcli/workflow/wakatime/__init__.py +0 -80
- {mcli_framework-7.10.1.dist-info → mcli_framework-7.10.2.dist-info}/WHEEL +0 -0
- {mcli_framework-7.10.1.dist-info → mcli_framework-7.10.2.dist-info}/entry_points.txt +0 -0
- {mcli_framework-7.10.1.dist-info → mcli_framework-7.10.2.dist-info}/licenses/LICENSE +0 -0
- {mcli_framework-7.10.1.dist-info → mcli_framework-7.10.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Validation utilities for workflow notebooks.
|
|
3
|
+
|
|
4
|
+
Provides validation for:
|
|
5
|
+
- JSON schema compliance
|
|
6
|
+
- Code syntax checking
|
|
7
|
+
- Shell script validation
|
|
8
|
+
- MCLI API validation
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import ast
|
|
12
|
+
import subprocess
|
|
13
|
+
import tempfile
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any, Dict, List, Optional
|
|
16
|
+
|
|
17
|
+
from mcli.lib.logger.logger import get_logger
|
|
18
|
+
|
|
19
|
+
from .schema import NOTEBOOK_SCHEMA, CellLanguage, CellType, WorkflowNotebook
|
|
20
|
+
|
|
21
|
+
logger = get_logger()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class NotebookValidator:
|
|
25
|
+
"""Validator for workflow notebooks."""
|
|
26
|
+
|
|
27
|
+
def __init__(self):
|
|
28
|
+
self.schema_errors: List[str] = []
|
|
29
|
+
self.syntax_errors: List[str] = []
|
|
30
|
+
|
|
31
|
+
def validate_schema(self, notebook: WorkflowNotebook) -> bool:
|
|
32
|
+
"""
|
|
33
|
+
Validate notebook against JSON schema.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
notebook: WorkflowNotebook to validate
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
True if valid, False otherwise
|
|
40
|
+
"""
|
|
41
|
+
self.schema_errors = []
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
import jsonschema
|
|
45
|
+
|
|
46
|
+
data = notebook.to_dict()
|
|
47
|
+
jsonschema.validate(instance=data, schema=NOTEBOOK_SCHEMA)
|
|
48
|
+
return True
|
|
49
|
+
|
|
50
|
+
except ImportError:
|
|
51
|
+
# jsonschema not installed, do basic validation
|
|
52
|
+
logger.warning("jsonschema not installed, performing basic validation")
|
|
53
|
+
return self._basic_schema_validation(notebook)
|
|
54
|
+
|
|
55
|
+
except Exception as e:
|
|
56
|
+
self.schema_errors.append(str(e))
|
|
57
|
+
return False
|
|
58
|
+
|
|
59
|
+
def _basic_schema_validation(self, notebook: WorkflowNotebook) -> bool:
|
|
60
|
+
"""Basic schema validation without jsonschema library."""
|
|
61
|
+
valid = True
|
|
62
|
+
|
|
63
|
+
# Check required fields
|
|
64
|
+
if not notebook.metadata.mcli.name:
|
|
65
|
+
self.schema_errors.append("Missing required field: metadata.mcli.name")
|
|
66
|
+
valid = False
|
|
67
|
+
|
|
68
|
+
if notebook.nbformat != 4:
|
|
69
|
+
self.schema_errors.append(f"Invalid nbformat: {notebook.nbformat} (expected 4)")
|
|
70
|
+
valid = False
|
|
71
|
+
|
|
72
|
+
# Validate cells
|
|
73
|
+
for i, cell in enumerate(notebook.cells):
|
|
74
|
+
if not cell.cell_type:
|
|
75
|
+
self.schema_errors.append(f"Cell {i}: Missing cell_type")
|
|
76
|
+
valid = False
|
|
77
|
+
|
|
78
|
+
if not cell.source and not isinstance(cell.source, (str, list)):
|
|
79
|
+
self.schema_errors.append(f"Cell {i}: Missing or invalid source")
|
|
80
|
+
valid = False
|
|
81
|
+
|
|
82
|
+
return valid
|
|
83
|
+
|
|
84
|
+
def validate_syntax(self, notebook: WorkflowNotebook) -> bool:
|
|
85
|
+
"""
|
|
86
|
+
Validate code syntax in all code cells.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
notebook: WorkflowNotebook to validate
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
True if all code is syntactically valid, False otherwise
|
|
93
|
+
"""
|
|
94
|
+
self.syntax_errors = []
|
|
95
|
+
all_valid = True
|
|
96
|
+
|
|
97
|
+
for i, cell in enumerate(notebook.cells):
|
|
98
|
+
if cell.cell_type != CellType.CODE:
|
|
99
|
+
continue
|
|
100
|
+
|
|
101
|
+
language = cell.language
|
|
102
|
+
code = cell.source_text
|
|
103
|
+
|
|
104
|
+
if language == CellLanguage.PYTHON:
|
|
105
|
+
if not self._validate_python_syntax(code, i):
|
|
106
|
+
all_valid = False
|
|
107
|
+
|
|
108
|
+
elif language in (CellLanguage.SHELL, CellLanguage.BASH, CellLanguage.ZSH):
|
|
109
|
+
if not self._validate_shell_syntax(code, i):
|
|
110
|
+
all_valid = False
|
|
111
|
+
|
|
112
|
+
return all_valid
|
|
113
|
+
|
|
114
|
+
def _validate_python_syntax(self, code: str, cell_index: int) -> bool:
|
|
115
|
+
"""Validate Python code syntax."""
|
|
116
|
+
try:
|
|
117
|
+
ast.parse(code)
|
|
118
|
+
return True
|
|
119
|
+
except SyntaxError as e:
|
|
120
|
+
self.syntax_errors.append(
|
|
121
|
+
f"Cell {cell_index} (Python): Syntax error at line {e.lineno}: {e.msg}"
|
|
122
|
+
)
|
|
123
|
+
return False
|
|
124
|
+
except Exception as e:
|
|
125
|
+
self.syntax_errors.append(f"Cell {cell_index} (Python): {str(e)}")
|
|
126
|
+
return False
|
|
127
|
+
|
|
128
|
+
def _validate_shell_syntax(self, code: str, cell_index: int) -> bool:
|
|
129
|
+
"""Validate shell script syntax using bash -n."""
|
|
130
|
+
try:
|
|
131
|
+
# Create temporary file with shell script
|
|
132
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".sh", delete=False) as f:
|
|
133
|
+
f.write(code)
|
|
134
|
+
temp_path = f.name
|
|
135
|
+
|
|
136
|
+
try:
|
|
137
|
+
# Use bash -n to check syntax without executing
|
|
138
|
+
result = subprocess.run(
|
|
139
|
+
["bash", "-n", temp_path],
|
|
140
|
+
capture_output=True,
|
|
141
|
+
text=True,
|
|
142
|
+
timeout=5,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
if result.returncode != 0:
|
|
146
|
+
error_msg = result.stderr.strip()
|
|
147
|
+
self.syntax_errors.append(f"Cell {cell_index} (Shell): {error_msg}")
|
|
148
|
+
return False
|
|
149
|
+
|
|
150
|
+
return True
|
|
151
|
+
|
|
152
|
+
finally:
|
|
153
|
+
# Clean up temp file
|
|
154
|
+
Path(temp_path).unlink(missing_ok=True)
|
|
155
|
+
|
|
156
|
+
except subprocess.TimeoutExpired:
|
|
157
|
+
self.syntax_errors.append(f"Cell {cell_index} (Shell): Validation timeout")
|
|
158
|
+
return False
|
|
159
|
+
except Exception as e:
|
|
160
|
+
self.syntax_errors.append(f"Cell {cell_index} (Shell): {str(e)}")
|
|
161
|
+
return False
|
|
162
|
+
|
|
163
|
+
def validate_mcli_apis(self, notebook: WorkflowNotebook) -> bool:
|
|
164
|
+
"""
|
|
165
|
+
Validate MCLI API usage in code cells.
|
|
166
|
+
|
|
167
|
+
This checks for:
|
|
168
|
+
- Proper Click decorator usage
|
|
169
|
+
- MCLI library imports
|
|
170
|
+
- Common API patterns
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
notebook: WorkflowNotebook to validate
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
True if API usage is valid, False otherwise
|
|
177
|
+
"""
|
|
178
|
+
# TODO: Implement MCLI-specific API validation
|
|
179
|
+
# This could check for:
|
|
180
|
+
# - @click.command() or @click.group() decorators
|
|
181
|
+
# - Proper import statements
|
|
182
|
+
# - Common anti-patterns
|
|
183
|
+
return True
|
|
184
|
+
|
|
185
|
+
def get_all_errors(self) -> List[str]:
|
|
186
|
+
"""Get all validation errors."""
|
|
187
|
+
return self.schema_errors + self.syntax_errors
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class CodeLinter:
|
|
191
|
+
"""Linter for workflow notebook code."""
|
|
192
|
+
|
|
193
|
+
def __init__(self):
|
|
194
|
+
self.issues: List[Dict[str, any]] = []
|
|
195
|
+
|
|
196
|
+
def lint_python(self, code: str) -> List[Dict[str, any]]:
|
|
197
|
+
"""
|
|
198
|
+
Lint Python code using available linters.
|
|
199
|
+
|
|
200
|
+
Tries to use (in order):
|
|
201
|
+
1. flake8
|
|
202
|
+
2. pylint
|
|
203
|
+
3. Basic AST-based checks
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
List of lint issues
|
|
207
|
+
"""
|
|
208
|
+
self.issues = []
|
|
209
|
+
|
|
210
|
+
# Try flake8
|
|
211
|
+
try:
|
|
212
|
+
import flake8.api.legacy as flake8_api
|
|
213
|
+
|
|
214
|
+
style_guide = flake8_api.get_style_guide()
|
|
215
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
|
|
216
|
+
f.write(code)
|
|
217
|
+
temp_path = f.name
|
|
218
|
+
|
|
219
|
+
try:
|
|
220
|
+
report = style_guide.check_files([temp_path])
|
|
221
|
+
# Convert flake8 results to our format
|
|
222
|
+
# (this is simplified - actual implementation would parse flake8 output)
|
|
223
|
+
if report.total_errors > 0:
|
|
224
|
+
self.issues.append(
|
|
225
|
+
{
|
|
226
|
+
"severity": "warning",
|
|
227
|
+
"message": f"Found {report.total_errors} style issues",
|
|
228
|
+
"line": 0,
|
|
229
|
+
}
|
|
230
|
+
)
|
|
231
|
+
finally:
|
|
232
|
+
Path(temp_path).unlink(missing_ok=True)
|
|
233
|
+
|
|
234
|
+
except ImportError:
|
|
235
|
+
# flake8 not available, try basic checks
|
|
236
|
+
self._basic_python_lint(code)
|
|
237
|
+
|
|
238
|
+
return self.issues
|
|
239
|
+
|
|
240
|
+
def _basic_python_lint(self, code: str):
|
|
241
|
+
"""Basic Python linting using AST."""
|
|
242
|
+
try:
|
|
243
|
+
tree = ast.parse(code)
|
|
244
|
+
|
|
245
|
+
# Check for common issues
|
|
246
|
+
for node in ast.walk(tree):
|
|
247
|
+
# Check for unused imports (simplified)
|
|
248
|
+
if isinstance(node, ast.Import):
|
|
249
|
+
# TODO: Check if imports are actually used
|
|
250
|
+
pass
|
|
251
|
+
|
|
252
|
+
# Check for bare except
|
|
253
|
+
if isinstance(node, ast.ExceptHandler) and node.type is None:
|
|
254
|
+
self.issues.append(
|
|
255
|
+
{
|
|
256
|
+
"severity": "warning",
|
|
257
|
+
"message": "Bare except clause - consider specifying exception type",
|
|
258
|
+
"line": node.lineno,
|
|
259
|
+
}
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
except SyntaxError:
|
|
263
|
+
# Already caught by syntax validation
|
|
264
|
+
pass
|
|
265
|
+
|
|
266
|
+
def lint_shell(self, code: str) -> List[Dict[str, any]]:
|
|
267
|
+
"""
|
|
268
|
+
Lint shell script using shellcheck if available.
|
|
269
|
+
|
|
270
|
+
Returns:
|
|
271
|
+
List of lint issues
|
|
272
|
+
"""
|
|
273
|
+
self.issues = []
|
|
274
|
+
|
|
275
|
+
try:
|
|
276
|
+
# Create temporary file
|
|
277
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".sh", delete=False) as f:
|
|
278
|
+
f.write(code)
|
|
279
|
+
temp_path = f.name
|
|
280
|
+
|
|
281
|
+
try:
|
|
282
|
+
# Try to use shellcheck
|
|
283
|
+
result = subprocess.run(
|
|
284
|
+
["shellcheck", "-f", "json", temp_path],
|
|
285
|
+
capture_output=True,
|
|
286
|
+
text=True,
|
|
287
|
+
timeout=10,
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
if result.stdout:
|
|
291
|
+
import json
|
|
292
|
+
|
|
293
|
+
issues = json.loads(result.stdout)
|
|
294
|
+
self.issues = [
|
|
295
|
+
{
|
|
296
|
+
"severity": issue.get("level", "warning"),
|
|
297
|
+
"message": issue.get("message", ""),
|
|
298
|
+
"line": issue.get("line", 0),
|
|
299
|
+
"code": issue.get("code", ""),
|
|
300
|
+
}
|
|
301
|
+
for issue in issues
|
|
302
|
+
]
|
|
303
|
+
|
|
304
|
+
finally:
|
|
305
|
+
Path(temp_path).unlink(missing_ok=True)
|
|
306
|
+
|
|
307
|
+
except FileNotFoundError:
|
|
308
|
+
# shellcheck not installed
|
|
309
|
+
logger.debug("shellcheck not found, skipping shell linting")
|
|
310
|
+
except Exception as e:
|
|
311
|
+
logger.warning(f"Shell linting failed: {e}")
|
|
312
|
+
|
|
313
|
+
return self.issues
|
mcli/workflow/workflow.py
CHANGED
|
@@ -14,5 +14,19 @@ def workflow():
|
|
|
14
14
|
pass
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
# Add notebook subcommand
|
|
18
|
+
try:
|
|
19
|
+
from mcli.workflow.notebook.notebook_cmd import notebook
|
|
20
|
+
|
|
21
|
+
workflow.add_command(notebook)
|
|
22
|
+
except ImportError as e:
|
|
23
|
+
# Notebook commands not available
|
|
24
|
+
import sys
|
|
25
|
+
from mcli.lib.logger.logger import get_logger
|
|
26
|
+
|
|
27
|
+
logger = get_logger()
|
|
28
|
+
logger.debug(f"Notebook commands not available: {e}")
|
|
29
|
+
|
|
30
|
+
|
|
17
31
|
if __name__ == "__main__":
|
|
18
32
|
workflow()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcli-framework
|
|
3
|
-
Version: 7.10.
|
|
3
|
+
Version: 7.10.2
|
|
4
4
|
Summary: Portable workflow framework - transform any script into a versioned, schedulable command. Store in ~/.mcli/commands/, version with lockfile, run as daemon or cron job.
|
|
5
5
|
Author-email: Luis Fernandez de la Vara <luis@lefv.io>
|
|
6
6
|
Maintainer-email: Luis Fernandez de la Vara <luis@lefv.io>
|
|
@@ -160,6 +160,11 @@ Dynamic: license-file
|
|
|
160
160
|
|
|
161
161
|
# MCLI - Portable Workflow Framework
|
|
162
162
|
|
|
163
|
+
[](https://codecov.io/gh/gwicho38/mcli)
|
|
164
|
+
[](https://github.com/gwicho38/mcli/actions)
|
|
165
|
+
[](https://www.python.org/downloads/)
|
|
166
|
+
[](LICENSE)
|
|
167
|
+
|
|
163
168
|
**Transform any script into a versioned, portable, schedulable workflow command.**
|
|
164
169
|
|
|
165
170
|
MCLI is a modular CLI framework that lets you write scripts once and run them anywhere - as interactive commands, scheduled jobs, or background daemons. Your workflows live in `~/.mcli/commands/`, are versioned via lockfile, and completely decoupled from the engine source code.
|
|
@@ -170,6 +175,35 @@ Write a script. Store it. Version it. Run it anywhere. Schedule it. Share it.
|
|
|
170
175
|
|
|
171
176
|
No coupling to the engine. No vendor lock-in. Just portable workflows that work.
|
|
172
177
|
|
|
178
|
+
## 🚀 Visual Workflow Editing
|
|
179
|
+
|
|
180
|
+
Edit your workflow JSON files like Jupyter notebooks with our VSCode extension!
|
|
181
|
+
|
|
182
|
+
[](vscode-extension/)
|
|
183
|
+
[](vscode-extension/)
|
|
184
|
+
|
|
185
|
+
**Features:**
|
|
186
|
+
- 📝 Cell-based editing (Jupyter-like interface)
|
|
187
|
+
- ⚡ Live code execution (Python, Shell, Bash, Zsh, Fish)
|
|
188
|
+
- 🎯 Monaco editor with IntelliSense
|
|
189
|
+
- 📊 Rich markdown documentation cells
|
|
190
|
+
- 💾 Files stay as `.json` (git-friendly)
|
|
191
|
+
|
|
192
|
+
**Quick Install:**
|
|
193
|
+
```bash
|
|
194
|
+
# From VSCode Marketplace (pending publication)
|
|
195
|
+
code --install-extension gwicho38.mcli-framework
|
|
196
|
+
|
|
197
|
+
# Or install from VSIX
|
|
198
|
+
code --install-extension vscode-extension/mcli-framework-1.0.0.vsix
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Learn More:**
|
|
202
|
+
- [Extension README](vscode-extension/README.md) - Features and usage
|
|
203
|
+
- [Visual Editing Guide](README-VISUAL-EDITING.md) - Quick start
|
|
204
|
+
- [Installation Guide](vscode-extension/INSTALL.md) - Detailed setup
|
|
205
|
+
- [Workflow Notebooks Docs](docs/workflow-notebooks.md) - Complete guide
|
|
206
|
+
|
|
173
207
|
## ⚡ Quick Start
|
|
174
208
|
|
|
175
209
|
### Installation
|
|
@@ -450,7 +484,7 @@ Available workflows:
|
|
|
450
484
|
- **redis** - Redis cache management
|
|
451
485
|
- **videos** - Video processing and overlay removal
|
|
452
486
|
- **sync** - Multi-cloud synchronization
|
|
453
|
-
- **politician-trading** -
|
|
487
|
+
- **politician-trading** - Now available as standalone package: [politician-trading-tracker](https://github.com/gwicho38/politician-trading-tracker)
|
|
454
488
|
|
|
455
489
|
## 💡 Why MCLI?
|
|
456
490
|
|