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.

Files changed (32) hide show
  1. machineconfig/scripts/python/agents.py +12 -48
  2. machineconfig/scripts/python/ai/generate_files.py +197 -42
  3. machineconfig/scripts/python/croshell.py +5 -5
  4. machineconfig/scripts/python/devops_helpers/cli_config.py +1 -1
  5. machineconfig/scripts/python/devops_helpers/cli_self.py +3 -3
  6. machineconfig/scripts/python/devops_navigator.py +1 -890
  7. machineconfig/scripts/python/entry.py +17 -12
  8. machineconfig/scripts/python/fire_jobs.py +4 -4
  9. machineconfig/scripts/python/ftpx.py +4 -4
  10. machineconfig/scripts/python/helper_navigator/__init__.py +20 -0
  11. machineconfig/scripts/python/helper_navigator/command_builder.py +111 -0
  12. machineconfig/scripts/python/helper_navigator/command_detail.py +44 -0
  13. machineconfig/scripts/python/helper_navigator/command_tree.py +470 -0
  14. machineconfig/scripts/python/helper_navigator/data_models.py +28 -0
  15. machineconfig/scripts/python/helper_navigator/main_app.py +262 -0
  16. machineconfig/scripts/python/helper_navigator/search_bar.py +15 -0
  17. machineconfig/scripts/python/helpers_fire/template.sh +1 -0
  18. machineconfig/scripts/python/interactive.py +2 -2
  19. machineconfig/scripts/python/nw/mount_nfs +1 -1
  20. machineconfig/scripts/python/repos_helpers/count_lines_frontend.py +1 -1
  21. machineconfig/scripts/python/sessions.py +1 -1
  22. machineconfig/scripts/windows/mounts/mount_ssh.ps1 +1 -1
  23. machineconfig/setup_linux/web_shortcuts/interactive.sh +7 -7
  24. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +7 -7
  25. machineconfig/utils/ssh.py +2 -2
  26. {machineconfig-5.64.dist-info → machineconfig-5.66.dist-info}/METADATA +1 -2
  27. {machineconfig-5.64.dist-info → machineconfig-5.66.dist-info}/RECORD +30 -24
  28. machineconfig-5.66.dist-info/entry_points.txt +9 -0
  29. machineconfig/utils/ai/generate_file_checklist.py +0 -68
  30. machineconfig-5.64.dist-info/entry_points.txt +0 -9
  31. {machineconfig-5.64.dist-info → machineconfig-5.66.dist-info}/WHEEL +0 -0
  32. {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, Iterable, Optional, get_args
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: Optional[Path] = typer.Option(None, help="Path to the context file"),
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
- if context_path is not None:
63
- search_strategy = "file_path"
64
- target_file_path = context_path.expanduser().resolve()
65
- if not target_file_path.exists() or not target_file_path.is_file():
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 --context-path "{prompt_material_path}" \\
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
- agents_app.command("make-todo", no_args_is_help=False, help="Generate a markdown file listing all Python files in the repo")(generate_files)
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 main_from_parser():
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 .venv and __pycache__ directories
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 .venv or __pycache__ directories
19
- if any(part in {".venv", "__pycache__"} for part in path_parts):
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 generate_markdown_table(files: list[str]) -> str:
28
- """Generate markdown table with checkboxes."""
29
- header = "# Python Files Checklist\n\n"
30
- table = "| Index | File Path | Status |\n|-------|-----------|--------|\n"
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
- for index, file_path in enumerate(files, start=1):
33
- # Remove leading ./ if present
34
- clean_path = file_path.lstrip("./")
35
- table += f"| {index} | {clean_path} | - [ ] |\n"
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
- print(f"Repo root: {repo_root}")
48
- print(f"Output file: {output_file}")
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(repo_root)
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
- # Create 5 symlinks to repo_root at ~/code_copies/${repo_name}_copy_{i}
66
- import pathlib
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 main(
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.6"
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 arg_parser() -> None:
161
- typer.run(main)
160
+ def main() -> None:
161
+ typer.run(croshell)
162
162
 
163
163
 
164
164
  if __name__ == "__main__":
165
- arg_parser()
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 --no-dev --with machineconfig>=5.6,textual {path}")
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 --no-dev --with machineconfig>=5.6,textual {path}")
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)