machineconfig 5.64__py3-none-any.whl → 5.66__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 machineconfig might be problematic. Click here for more details.
- machineconfig/scripts/python/agents.py +12 -48
- machineconfig/scripts/python/ai/generate_files.py +197 -42
- machineconfig/scripts/python/croshell.py +5 -5
- machineconfig/scripts/python/devops_helpers/cli_config.py +1 -1
- machineconfig/scripts/python/devops_helpers/cli_self.py +3 -3
- machineconfig/scripts/python/devops_navigator.py +1 -890
- machineconfig/scripts/python/entry.py +17 -12
- machineconfig/scripts/python/fire_jobs.py +4 -4
- machineconfig/scripts/python/ftpx.py +4 -4
- machineconfig/scripts/python/helper_navigator/__init__.py +20 -0
- machineconfig/scripts/python/helper_navigator/command_builder.py +111 -0
- machineconfig/scripts/python/helper_navigator/command_detail.py +44 -0
- machineconfig/scripts/python/helper_navigator/command_tree.py +470 -0
- machineconfig/scripts/python/helper_navigator/data_models.py +28 -0
- machineconfig/scripts/python/helper_navigator/main_app.py +262 -0
- machineconfig/scripts/python/helper_navigator/search_bar.py +15 -0
- machineconfig/scripts/python/helpers_fire/template.sh +1 -0
- machineconfig/scripts/python/interactive.py +2 -2
- machineconfig/scripts/python/nw/mount_nfs +1 -1
- machineconfig/scripts/python/repos_helpers/count_lines_frontend.py +1 -1
- machineconfig/scripts/python/sessions.py +1 -1
- machineconfig/scripts/windows/mounts/mount_ssh.ps1 +1 -1
- machineconfig/setup_linux/web_shortcuts/interactive.sh +7 -7
- machineconfig/setup_windows/web_shortcuts/interactive.ps1 +7 -7
- machineconfig/utils/ssh.py +2 -2
- {machineconfig-5.64.dist-info → machineconfig-5.66.dist-info}/METADATA +1 -2
- {machineconfig-5.64.dist-info → machineconfig-5.66.dist-info}/RECORD +30 -24
- machineconfig-5.66.dist-info/entry_points.txt +9 -0
- machineconfig/utils/ai/generate_file_checklist.py +0 -68
- machineconfig-5.64.dist-info/entry_points.txt +0 -9
- {machineconfig-5.64.dist-info → machineconfig-5.66.dist-info}/WHEEL +0 -0
- {machineconfig-5.64.dist-info → machineconfig-5.66.dist-info}/top_level.txt +0 -0
|
@@ -3,20 +3,13 @@
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import cast,
|
|
6
|
+
from typing import cast, Optional, get_args
|
|
7
7
|
import typer
|
|
8
8
|
from machineconfig.scripts.python.helpers_fire.fire_agents_helper_types import AGENTS, MATCHINE, MODEL, PROVIDER
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
def _write_list_file(target: Path, files: Iterable[Path]) -> None:
|
|
12
|
-
target.parent.mkdir(parents=True, exist_ok=True)
|
|
13
|
-
target.write_text("\n".join(str(f) for f in files), encoding="utf-8")
|
|
14
|
-
|
|
15
|
-
|
|
16
11
|
def create(
|
|
17
|
-
context_path:
|
|
18
|
-
keyword_search: Optional[str] = typer.Option(None, help="Keyword to search in Python files"),
|
|
19
|
-
filename_pattern: Optional[str] = typer.Option(None, help="Filename pattern to match"),
|
|
12
|
+
context_path: Path = typer.Argument(..., help="Path to the context file"),
|
|
20
13
|
separator: str = typer.Option("\n", help="Separator for context"),
|
|
21
14
|
tasks_per_prompt: int = typer.Option(13, help="Number of tasks per prompt"),
|
|
22
15
|
|
|
@@ -34,17 +27,11 @@ def create(
|
|
|
34
27
|
):
|
|
35
28
|
|
|
36
29
|
from machineconfig.scripts.python.helpers_fire.fire_agents_help_launch import prep_agent_launch, get_agents_launch_layout
|
|
37
|
-
from machineconfig.scripts.python.helpers_fire.fire_agents_help_search import search_files_by_pattern, search_python_files
|
|
38
30
|
from machineconfig.scripts.python.helpers_fire.fire_agents_load_balancer import chunk_prompts
|
|
39
31
|
from machineconfig.utils.accessories import get_repo_root, randstr
|
|
40
32
|
import json
|
|
41
33
|
|
|
42
34
|
# validate mutual exclusive
|
|
43
|
-
context_options = [context_path, keyword_search, filename_pattern]
|
|
44
|
-
provided_context = [opt for opt in context_options if opt is not None]
|
|
45
|
-
if len(provided_context) != 1:
|
|
46
|
-
raise typer.BadParameter("Exactly one of --context-path, --keyword-search, --filename-pattern must be provided")
|
|
47
|
-
|
|
48
35
|
prompt_options = [prompt, prompt_path]
|
|
49
36
|
provided_prompt = [opt for opt in prompt_options if opt is not None]
|
|
50
37
|
if len(provided_prompt) != 1:
|
|
@@ -56,33 +43,12 @@ def create(
|
|
|
56
43
|
raise typer.Exit(1)
|
|
57
44
|
typer.echo(f"Operating @ {repo_root}")
|
|
58
45
|
|
|
59
|
-
search_strategy = ""
|
|
60
46
|
prompt_material_path = Path("")
|
|
61
47
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
raise typer.BadParameter(f"Invalid file path: {target_file_path}")
|
|
67
|
-
prompt_material_path = target_file_path
|
|
68
|
-
elif keyword_search is not None:
|
|
69
|
-
search_strategy = "keyword_search"
|
|
70
|
-
matching_files = search_python_files(repo_root, keyword_search)
|
|
71
|
-
if not matching_files:
|
|
72
|
-
typer.echo(f"💥 No .py files found containing keyword: {keyword_search}")
|
|
73
|
-
raise typer.Exit(1)
|
|
74
|
-
target_file_path = repo_root / ".ai" / "target_file.txt"
|
|
75
|
-
_write_list_file(target_file_path, matching_files)
|
|
76
|
-
prompt_material_path = target_file_path
|
|
77
|
-
elif filename_pattern is not None:
|
|
78
|
-
search_strategy = "filename_pattern"
|
|
79
|
-
matching_files = search_files_by_pattern(repo_root, filename_pattern)
|
|
80
|
-
if not matching_files:
|
|
81
|
-
typer.echo(f"💥 No files found matching pattern: {filename_pattern}")
|
|
82
|
-
raise typer.Exit(1)
|
|
83
|
-
target_file_path = repo_root / ".ai" / "target_file.txt"
|
|
84
|
-
_write_list_file(target_file_path, matching_files)
|
|
85
|
-
prompt_material_path = target_file_path
|
|
48
|
+
target_file_path = context_path.expanduser().resolve()
|
|
49
|
+
if not target_file_path.exists() or not target_file_path.is_file():
|
|
50
|
+
raise typer.BadParameter(f"Invalid file path: {target_file_path}")
|
|
51
|
+
prompt_material_path = target_file_path
|
|
86
52
|
|
|
87
53
|
if prompt_path is not None:
|
|
88
54
|
prompt_prefix = prompt_path.read_text(encoding="utf-8")
|
|
@@ -103,8 +69,7 @@ def create(
|
|
|
103
69
|
layoutfile = get_agents_launch_layout(session_root=agents_dir)
|
|
104
70
|
regenerate_py_code = f"""
|
|
105
71
|
#!/usr/bin/env uv run --python 3.14 --with machineconfig
|
|
106
|
-
agents create
|
|
107
|
-
--{search_strategy} "{context_path or keyword_search or filename_pattern}" \\
|
|
72
|
+
agents create "{prompt_material_path}" \\
|
|
108
73
|
--prompt-path "{prompt_path or ''}" \\
|
|
109
74
|
--agent "{agent_selected}" \\
|
|
110
75
|
--machine "{machine}" \\
|
|
@@ -187,20 +152,19 @@ def init_config():
|
|
|
187
152
|
from machineconfig.scripts.python.ai.initai import add_ai_configs
|
|
188
153
|
add_ai_configs(repo_root=Path.cwd())
|
|
189
154
|
|
|
190
|
-
def generate_files():
|
|
191
|
-
from machineconfig.scripts.python.ai.generate_files import main
|
|
192
|
-
main()
|
|
193
|
-
|
|
194
155
|
def get_app():
|
|
195
156
|
agents_app = typer.Typer(help="🤖 AI Agents management subcommands")
|
|
196
157
|
agents_app.command("create", no_args_is_help=True, help="Create agents layout file, ready to run.")(create)
|
|
197
158
|
agents_app.command("collect", no_args_is_help=True, help="Collect all agent materials into a single file.")(collect)
|
|
198
159
|
agents_app.command("make-template", no_args_is_help=False, help="Create a template for fire agents")(template)
|
|
199
160
|
agents_app.command("make-config", no_args_is_help=False, help="Initialize AI configurations in the current repository")(init_config)
|
|
200
|
-
|
|
161
|
+
from machineconfig.scripts.python.ai.generate_files import main
|
|
162
|
+
agents_app.command("make-todo", no_args_is_help=True, help="Generate a markdown file listing all Python files in the repo")(main)
|
|
163
|
+
from machineconfig.scripts.python.ai.generate_files import create_symlink_command
|
|
164
|
+
agents_app.command(name="make-symlinks", no_args_is_help=True, help="Create symlinks to the current repo in ~/code_copies/")(create_symlink_command)
|
|
201
165
|
return agents_app
|
|
202
166
|
|
|
203
|
-
def
|
|
167
|
+
def main():
|
|
204
168
|
agents_app = get_app()
|
|
205
169
|
import sys
|
|
206
170
|
if len(sys.argv) == 1:
|
|
@@ -1,22 +1,38 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Script to generate a markdown table with checkboxes for all Python files in the repo."""
|
|
2
|
+
"""Script to generate a markdown table with checkboxes for all Python and shell files in the repo."""
|
|
3
3
|
|
|
4
4
|
from pathlib import Path
|
|
5
|
+
from typing import Annotated, Literal
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
from rich.panel import Panel
|
|
8
|
+
import typer
|
|
9
|
+
import subprocess
|
|
5
10
|
|
|
6
11
|
|
|
7
|
-
def get_python_files(repo_root: Path) -> list[str]:
|
|
12
|
+
def get_python_files(repo_root: Path, exclude_init: bool = False) -> list[str]:
|
|
8
13
|
"""Get all Python files relative to repo root."""
|
|
14
|
+
excluded_parts = {".venv", "__pycache__", ".git", "build", "dist"}
|
|
15
|
+
excluded_patterns = {"*.egg-info"}
|
|
16
|
+
|
|
9
17
|
# Get all .py files recursively
|
|
10
18
|
py_files = list(repo_root.glob("**/*.py"))
|
|
11
19
|
|
|
12
|
-
# Filter out files in
|
|
20
|
+
# Filter out files in excluded directories
|
|
13
21
|
filtered_files = []
|
|
14
22
|
for file_path in py_files:
|
|
15
23
|
relative_path = file_path.relative_to(repo_root)
|
|
16
24
|
path_parts = relative_path.parts
|
|
17
25
|
|
|
18
|
-
# Skip files in
|
|
19
|
-
if any(part in
|
|
26
|
+
# Skip files in excluded directories
|
|
27
|
+
if any(part in excluded_parts for part in path_parts):
|
|
28
|
+
continue
|
|
29
|
+
|
|
30
|
+
# Skip files matching excluded patterns
|
|
31
|
+
if any(file_path.match(f"**/{pattern}/**") for pattern in excluded_patterns):
|
|
32
|
+
continue
|
|
33
|
+
|
|
34
|
+
# Skip __init__.py files if requested
|
|
35
|
+
if exclude_init and file_path.name == "__init__.py":
|
|
20
36
|
continue
|
|
21
37
|
|
|
22
38
|
filtered_files.append(str(relative_path))
|
|
@@ -24,60 +40,199 @@ def get_python_files(repo_root: Path) -> list[str]:
|
|
|
24
40
|
return sorted(filtered_files)
|
|
25
41
|
|
|
26
42
|
|
|
27
|
-
def
|
|
28
|
-
"""
|
|
29
|
-
|
|
30
|
-
|
|
43
|
+
def get_shell_files(repo_root: Path) -> list[str]:
|
|
44
|
+
"""Get all shell script files relative to repo root."""
|
|
45
|
+
excluded_parts = {".venv", "__pycache__", ".git", "build", "dist"}
|
|
46
|
+
excluded_patterns = {"*.egg-info"}
|
|
47
|
+
|
|
48
|
+
# Get all .sh files recursively
|
|
49
|
+
sh_files = list(repo_root.glob("**/*.sh"))
|
|
50
|
+
|
|
51
|
+
# Filter out files in excluded directories
|
|
52
|
+
filtered_files = []
|
|
53
|
+
for file_path in sh_files:
|
|
54
|
+
relative_path = file_path.relative_to(repo_root)
|
|
55
|
+
path_parts = relative_path.parts
|
|
56
|
+
|
|
57
|
+
# Skip files in excluded directories
|
|
58
|
+
if any(part in excluded_parts for part in path_parts):
|
|
59
|
+
continue
|
|
60
|
+
|
|
61
|
+
# Skip files matching excluded patterns
|
|
62
|
+
if any(file_path.match(f"**/{pattern}/**") for pattern in excluded_patterns):
|
|
63
|
+
continue
|
|
64
|
+
|
|
65
|
+
filtered_files.append(str(relative_path))
|
|
66
|
+
|
|
67
|
+
return sorted(filtered_files)
|
|
68
|
+
|
|
31
69
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
70
|
+
def count_lines(file_path: Path) -> int:
|
|
71
|
+
"""Count the number of lines in a file."""
|
|
72
|
+
try:
|
|
73
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
74
|
+
return sum(1 for _ in f)
|
|
75
|
+
except (IOError, UnicodeDecodeError):
|
|
76
|
+
return 0
|
|
36
77
|
|
|
37
|
-
return header + table
|
|
38
78
|
|
|
79
|
+
def is_git_repository(path: Path) -> bool:
|
|
80
|
+
"""Check if the given path is part of a git repository."""
|
|
81
|
+
try:
|
|
82
|
+
result = subprocess.run(
|
|
83
|
+
["git", "rev-parse", "--git-dir"],
|
|
84
|
+
cwd=path,
|
|
85
|
+
capture_output=True,
|
|
86
|
+
text=True,
|
|
87
|
+
check=False
|
|
88
|
+
)
|
|
89
|
+
return result.returncode == 0
|
|
90
|
+
except (subprocess.SubprocessError, FileNotFoundError):
|
|
91
|
+
return False
|
|
39
92
|
|
|
40
|
-
def main() -> None:
|
|
41
|
-
"""Main function."""
|
|
42
|
-
repo_root = Path.cwd()
|
|
43
|
-
if not repo_root.joinpath("pyproject.toml").exists():
|
|
44
|
-
raise RuntimeError(f" {repo_root} Not a repo root")
|
|
45
|
-
output_file = repo_root / ".ai" / "all_files_with_index.md"
|
|
46
93
|
|
|
47
|
-
|
|
48
|
-
|
|
94
|
+
def filter_files_by_name(files: list[str], pattern: str) -> list[str]:
|
|
95
|
+
"""Filter files by filename containing the pattern."""
|
|
96
|
+
return [f for f in files if pattern in f]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def filter_files_by_content(repo_root: Path, files: list[str], keyword: str) -> list[str]:
|
|
100
|
+
"""Filter files by content containing the keyword."""
|
|
101
|
+
filtered_files = []
|
|
102
|
+
for file_path in files:
|
|
103
|
+
full_path = repo_root / file_path
|
|
104
|
+
try:
|
|
105
|
+
with open(full_path, 'r', encoding='utf-8') as f:
|
|
106
|
+
content = f.read()
|
|
107
|
+
if keyword in content:
|
|
108
|
+
filtered_files.append(file_path)
|
|
109
|
+
except (IOError, UnicodeDecodeError):
|
|
110
|
+
# Skip files that can't be read
|
|
111
|
+
continue
|
|
112
|
+
return filtered_files
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def generate_markdown_table(python_files: list[str], shell_files: list[str], repo_root: Path, include_line_count: bool = False) -> str:
|
|
116
|
+
"""Generate markdown table with checkboxes."""
|
|
117
|
+
header = "# File Checklist\n\n"
|
|
118
|
+
|
|
119
|
+
content = ""
|
|
120
|
+
|
|
121
|
+
if python_files:
|
|
122
|
+
content += "## Python Files\n\n"
|
|
123
|
+
if include_line_count:
|
|
124
|
+
# Calculate line counts and sort by descending line count
|
|
125
|
+
python_with_counts = [(file, count_lines(repo_root / file)) for file in python_files]
|
|
126
|
+
python_with_counts.sort(key=lambda x: x[1], reverse=True)
|
|
127
|
+
python_files = [file for file, _ in python_with_counts]
|
|
128
|
+
|
|
129
|
+
content += "| Index | File Path | Line Count | Status |\n|-------|-----------|------------|--------|\n"
|
|
130
|
+
else:
|
|
131
|
+
content += "| Index | File Path | Status |\n|-------|-----------|--------|\n"
|
|
132
|
+
for index, file_path in enumerate(python_files, start=1):
|
|
133
|
+
clean_path = file_path.lstrip("./")
|
|
134
|
+
if include_line_count:
|
|
135
|
+
line_count = count_lines(repo_root / file_path)
|
|
136
|
+
content += f"| {index} | {clean_path} | {line_count} | - [ ] |\n"
|
|
137
|
+
else:
|
|
138
|
+
content += f"| {index} | {clean_path} | - [ ] |\n"
|
|
139
|
+
|
|
140
|
+
if shell_files:
|
|
141
|
+
content += "\n## Shell Script Files\n\n"
|
|
142
|
+
if include_line_count:
|
|
143
|
+
# Calculate line counts and sort by descending line count
|
|
144
|
+
shell_with_counts = [(file, count_lines(repo_root / file)) for file in shell_files]
|
|
145
|
+
shell_with_counts.sort(key=lambda x: x[1], reverse=True)
|
|
146
|
+
shell_files = [file for file, _ in shell_with_counts]
|
|
147
|
+
|
|
148
|
+
content += "| Index | File Path | Line Count | Status |\n|-------|-----------|------------|--------|\n"
|
|
149
|
+
else:
|
|
150
|
+
content += "| Index | File Path | Status |\n|-------|-----------|--------|\n"
|
|
151
|
+
for index, file_path in enumerate(shell_files, start=1):
|
|
152
|
+
clean_path = file_path.lstrip("./")
|
|
153
|
+
if include_line_count:
|
|
154
|
+
line_count = count_lines(repo_root / file_path)
|
|
155
|
+
content += f"| {index} | {clean_path} | {line_count} | - [ ] |\n"
|
|
156
|
+
else:
|
|
157
|
+
content += f"| {index} | {clean_path} | - [ ] |\n"
|
|
158
|
+
|
|
159
|
+
return header + content
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def create_repo_symlinks(repo_root: Path) -> None:
|
|
163
|
+
"""Create 5 symlinks to repo_root at ~/code_copies/${repo_name}_copy_{i}."""
|
|
164
|
+
repo_name: str = repo_root.name
|
|
165
|
+
symlink_dir: Path = Path.home() / "code_copies"
|
|
166
|
+
symlink_dir.mkdir(exist_ok=True)
|
|
167
|
+
for i in range(1, 6):
|
|
168
|
+
symlink_path: Path = symlink_dir / f"{repo_name}_copy_{i}"
|
|
169
|
+
if symlink_path.exists() or symlink_path.is_symlink():
|
|
170
|
+
symlink_path.unlink()
|
|
171
|
+
symlink_path.symlink_to(repo_root, target_is_directory=True)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def main(
|
|
175
|
+
pattern: Annotated[str, typer.Argument(help="Pattern or keyword to match files by")],
|
|
176
|
+
repo: Annotated[str, typer.Argument(help="Repository path. Can be any directory within a git repository.")] = str(Path.cwd()),
|
|
177
|
+
strategy: Annotated[Literal["name", "keywords"], typer.Option("--strategy", help="Strategy to filter files: 'name' for filename matching, 'keywords' for content matching")] = "name",
|
|
178
|
+
exclude_init: Annotated[bool, typer.Option("--exclude-init", help="Exclude __init__.py files from the checklist")] = False,
|
|
179
|
+
include_line_count: Annotated[bool, typer.Option("--line-count", help="Include line count column in the markdown table")] = False,
|
|
180
|
+
output_path: Annotated[str, typer.Option("--output-path", help="Path to output the markdown file relative to repo root")] = ".ai/todo/all_files_with_index.md",
|
|
181
|
+
) -> None:
|
|
182
|
+
"""Generate markdown checklist with Python and shell script files in the repository filtered by pattern."""
|
|
183
|
+
repo_path = Path(repo).expanduser().absolute()
|
|
184
|
+
if not is_git_repository(repo_path):
|
|
185
|
+
console = Console()
|
|
186
|
+
console.print(Panel(f"❌ ERROR | Not a git repository or not in a git repository: {repo_path}", border_style="bold red", expand=False))
|
|
187
|
+
raise typer.Exit(code=1)
|
|
188
|
+
|
|
189
|
+
output_file = repo_path / output_path
|
|
49
190
|
|
|
50
191
|
# Ensure output directory exists
|
|
51
192
|
output_file.parent.mkdir(parents=True, exist_ok=True)
|
|
52
193
|
|
|
53
|
-
# Get Python files
|
|
54
|
-
python_files = get_python_files(
|
|
194
|
+
# Get Python and shell files
|
|
195
|
+
python_files = get_python_files(repo_path, exclude_init=exclude_init)
|
|
196
|
+
shell_files = get_shell_files(repo_path)
|
|
197
|
+
|
|
198
|
+
# Apply filtering based on strategy
|
|
199
|
+
if strategy == "name":
|
|
200
|
+
python_files = filter_files_by_name(python_files, pattern)
|
|
201
|
+
shell_files = filter_files_by_name(shell_files, pattern)
|
|
202
|
+
elif strategy == "keywords":
|
|
203
|
+
python_files = filter_files_by_content(repo_path, python_files, pattern)
|
|
204
|
+
shell_files = filter_files_by_content(repo_path, shell_files, pattern)
|
|
205
|
+
|
|
206
|
+
print(f"Repo path: {repo_path}")
|
|
207
|
+
print(f"Strategy: {strategy}")
|
|
208
|
+
print(f"Pattern: {pattern}")
|
|
55
209
|
print(f"Found {len(python_files)} Python files")
|
|
210
|
+
print(f"Found {len(shell_files)} Shell script files")
|
|
56
211
|
|
|
57
212
|
# Generate markdown
|
|
58
|
-
markdown_content = generate_markdown_table(python_files)
|
|
59
|
-
print(f"Generated markdown content length: {len(markdown_content)}")
|
|
213
|
+
markdown_content = generate_markdown_table(python_files, shell_files, repo_path, include_line_count)
|
|
60
214
|
|
|
61
215
|
# Write to file
|
|
62
216
|
output_file.write_text(markdown_content)
|
|
63
|
-
print(f"Generated {output_file} with {len(python_files)} Python files")
|
|
64
217
|
|
|
65
|
-
|
|
66
|
-
|
|
218
|
+
console = Console()
|
|
219
|
+
console.print(Panel(f"""✅ SUCCESS | Markdown checklist generated successfully!
|
|
220
|
+
📄 File Location: {output_file}
|
|
221
|
+
🐍 Python files: {len(python_files)}
|
|
222
|
+
🔧 Shell files: {len(shell_files)}""", border_style="bold blue", expand=False))
|
|
67
223
|
|
|
68
|
-
# import os
|
|
69
|
-
repo_root = pathlib.Path.cwd().resolve()
|
|
70
|
-
repo_name: str = pathlib.Path(repo_root).name
|
|
71
|
-
symlink_dir: pathlib.Path = pathlib.Path.home() / "code_copies"
|
|
72
|
-
symlink_dir.mkdir(exist_ok=True)
|
|
73
|
-
for i in range(1, 6):
|
|
74
|
-
symlink_path: pathlib.Path = symlink_dir / f"{repo_name}_copy_{i}"
|
|
75
|
-
if symlink_path.exists() or symlink_path.is_symlink():
|
|
76
|
-
symlink_path.unlink()
|
|
77
|
-
symlink_path.symlink_to(repo_root, target_is_directory=True)
|
|
78
|
-
# Windows equivalent (comment):
|
|
79
|
-
# for /l %i in (1,1,5) do mklink /D "%USERPROFILE%\code_copies\{repo_name}_copy_%i" "C:\path\to\repo_root"
|
|
80
224
|
|
|
225
|
+
def create_symlink_command(num: Annotated[int, typer.Argument(help="Number of symlinks to create (1-5).")] = 5) -> None:
|
|
226
|
+
"""Create 5 symlinks to repo_root at ~/code_copies/${repo_name}_copy_{i}."""
|
|
227
|
+
if num < 1 or num > 5:
|
|
228
|
+
console = Console()
|
|
229
|
+
console.print(Panel("❌ ERROR | Number of symlinks must be between 1 and 5.", border_style="bold red", expand=False))
|
|
230
|
+
raise typer.Exit(code=1)
|
|
231
|
+
repo_root = Path.cwd().absolute()
|
|
232
|
+
create_repo_symlinks(repo_root)
|
|
233
|
+
console = Console()
|
|
234
|
+
console.print(Panel(f"✅ SUCCESS | Created {num} symlinks to {repo_root} in ~/code_copies/", border_style="bold green", expand=False))
|
|
81
235
|
|
|
82
236
|
if __name__ == "__main__":
|
|
83
|
-
main
|
|
237
|
+
typer.run(main)
|
|
238
|
+
# typer.run(create_symlink_command)
|
|
@@ -62,7 +62,7 @@ except Exception as e:
|
|
|
62
62
|
|
|
63
63
|
|
|
64
64
|
|
|
65
|
-
def
|
|
65
|
+
def croshell(
|
|
66
66
|
python: Annotated[bool, typer.Option("--python", "-p", help="flag to use python over IPython.")] = False,
|
|
67
67
|
fzf: Annotated[bool, typer.Option("--fzf", "-F", help="search with fuzzy finder for python scripts and run them")] = False,
|
|
68
68
|
profile: Annotated[Optional[str], typer.Option("--profile", "-P", help="ipython profile to use, defaults to default profile.")] = None,
|
|
@@ -150,16 +150,16 @@ from pathlib import Path
|
|
|
150
150
|
else:
|
|
151
151
|
console.print(Panel("❌ Could not determine the local machineconfig repo root. Please ensure the `REPO_ROOT` in `source_of_truth.py` is correctly set to the local path of the machineconfig repo, or do not use the `--local` flag.", title="Error", border_style="red"))
|
|
152
152
|
return
|
|
153
|
-
else: ve_line = "--with machineconfig[plot]>=5.
|
|
153
|
+
else: ve_line = "--with machineconfig[plot]>=5.65"
|
|
154
154
|
fire_line = f"uv run --python 3.14 {ve_line} {interpreter} {interactivity} {profile} {str(pyfile)}"
|
|
155
155
|
|
|
156
156
|
from machineconfig.utils.code import run_shell_script
|
|
157
157
|
run_shell_script(fire_line, clean_env=False)
|
|
158
158
|
|
|
159
159
|
|
|
160
|
-
def
|
|
161
|
-
typer.run(
|
|
160
|
+
def main() -> None:
|
|
161
|
+
typer.run(croshell)
|
|
162
162
|
|
|
163
163
|
|
|
164
164
|
if __name__ == "__main__":
|
|
165
|
-
|
|
165
|
+
main()
|
|
@@ -48,7 +48,7 @@ def path():
|
|
|
48
48
|
from pathlib import Path
|
|
49
49
|
path = Path(navigator.__file__).resolve().parent.joinpath("path_manager_tui.py")
|
|
50
50
|
from machineconfig.utils.code import run_shell_script
|
|
51
|
-
run_shell_script(f"uv run --
|
|
51
|
+
run_shell_script(f"uv run --with machineconfig>=5.65,textual {path}")
|
|
52
52
|
|
|
53
53
|
@config_apps.command(no_args_is_help=False)
|
|
54
54
|
def pwsh_theme():
|
|
@@ -31,9 +31,9 @@ def install():
|
|
|
31
31
|
# main_public_from_parser()
|
|
32
32
|
import platform
|
|
33
33
|
if platform.system() == "Windows":
|
|
34
|
-
run_shell_script(r"""$HOME\.local\bin\uv.exe tool install machineconfig""")
|
|
34
|
+
run_shell_script(r"""$HOME\.local\bin\uv.exe tool install machineconfig>=5.65""")
|
|
35
35
|
else:
|
|
36
|
-
run_shell_script("""$HOME/.local/bin/uv tool install machineconfig""")
|
|
36
|
+
run_shell_script("""$HOME/.local/bin/uv tool install machineconfig>=5.65""")
|
|
37
37
|
|
|
38
38
|
@cli_app.command(no_args_is_help=False)
|
|
39
39
|
def navigate():
|
|
@@ -42,7 +42,7 @@ def navigate():
|
|
|
42
42
|
from pathlib import Path
|
|
43
43
|
path = Path(navigator.__file__).resolve().parent.joinpath("devops_navigator.py")
|
|
44
44
|
from machineconfig.utils.code import run_shell_script
|
|
45
|
-
run_shell_script(f"uv run --
|
|
45
|
+
run_shell_script(f"uv run --with machineconfig>=5.65,textual {path}")
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
@cli_app.command(no_args_is_help=True)
|