machineconfig 7.64__py3-none-any.whl → 7.83__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 (104) hide show
  1. machineconfig/cluster/sessions_managers/utils/maker.py +4 -2
  2. machineconfig/jobs/installer/custom/yazi.py +120 -0
  3. machineconfig/jobs/installer/custom_dev/nerdfont.py +1 -1
  4. machineconfig/jobs/installer/custom_dev/nerfont_windows_helper.py +26 -12
  5. machineconfig/jobs/installer/custom_dev/sysabc.py +26 -5
  6. machineconfig/jobs/installer/installer_data.json +232 -96
  7. machineconfig/jobs/installer/powershell_scripts/install_fonts.ps1 +129 -34
  8. machineconfig/profile/create_helper.py +0 -12
  9. machineconfig/profile/create_links_export.py +2 -2
  10. machineconfig/profile/mapper.toml +2 -2
  11. machineconfig/scripts/__init__.py +0 -4
  12. machineconfig/scripts/python/agents.py +22 -17
  13. machineconfig/scripts/python/ai/solutions/copilot/instructions/python/dev.instructions.md +4 -0
  14. machineconfig/scripts/python/croshell.py +22 -17
  15. machineconfig/scripts/python/devops.py +1 -1
  16. machineconfig/scripts/python/devops_navigator.py +0 -4
  17. machineconfig/scripts/python/env_manager/env_manager_tui.py +204 -0
  18. machineconfig/scripts/python/env_manager/path_manager_tui.py +1 -1
  19. machineconfig/scripts/python/fire_jobs.py +13 -13
  20. machineconfig/scripts/python/ftpx.py +36 -12
  21. machineconfig/scripts/python/helpers/ast_search.py +74 -0
  22. machineconfig/scripts/python/helpers/qr_code.py +166 -0
  23. machineconfig/scripts/python/helpers/repo_rag.py +325 -0
  24. machineconfig/scripts/python/helpers/symantic_search.py +25 -0
  25. machineconfig/scripts/python/helpers_cloud/cloud_copy.py +28 -21
  26. machineconfig/scripts/python/helpers_cloud/cloud_helpers.py +1 -1
  27. machineconfig/scripts/python/helpers_cloud/cloud_mount.py +19 -17
  28. machineconfig/scripts/python/helpers_cloud/cloud_sync.py +8 -7
  29. machineconfig/scripts/python/helpers_croshell/start_slidev.py +6 -7
  30. machineconfig/scripts/python/helpers_devops/cli_config.py +10 -0
  31. machineconfig/scripts/python/helpers_devops/cli_nw.py +90 -10
  32. machineconfig/scripts/python/helpers_devops/cli_self.py +8 -7
  33. machineconfig/scripts/python/helpers_devops/cli_share_file.py +7 -7
  34. machineconfig/scripts/python/helpers_devops/cli_share_server.py +12 -11
  35. machineconfig/scripts/python/helpers_devops/cli_terminal.py +8 -10
  36. machineconfig/scripts/python/helpers_devops/cli_utils.py +2 -1
  37. machineconfig/scripts/python/helpers_devops/devops_status.py +7 -19
  38. machineconfig/scripts/python/helpers_fire_command/fire_jobs_route_helper.py +20 -9
  39. machineconfig/scripts/python/helpers_msearch/scripts_linux/fzfg +2 -2
  40. machineconfig/scripts/python/helpers_msearch/scripts_windows/fzfg.ps1 +58 -1
  41. machineconfig/scripts/python/helpers_navigator/command_tree.py +50 -18
  42. machineconfig/scripts/python/helpers_repos/cloud_repo_sync.py +5 -3
  43. machineconfig/scripts/python/helpers_repos/count_lines.py +40 -11
  44. machineconfig/scripts/python/helpers_repos/count_lines_frontend.py +1 -1
  45. machineconfig/scripts/python/helpers_utils/download.py +4 -3
  46. machineconfig/scripts/python/helpers_utils/path.py +87 -34
  47. machineconfig/scripts/python/interactive.py +1 -1
  48. machineconfig/scripts/python/{machineconfig.py → mcfg_entry.py} +4 -0
  49. machineconfig/scripts/python/msearch.py +55 -6
  50. machineconfig/scripts/python/nw/address.py +132 -0
  51. machineconfig/scripts/python/nw/devops_add_ssh_key.py +8 -5
  52. machineconfig/scripts/python/terminal.py +2 -2
  53. machineconfig/scripts/python/utils.py +12 -11
  54. machineconfig/scripts/windows/mounts/mount_ssh.ps1 +1 -1
  55. machineconfig/settings/lf/windows/lfcd.ps1 +1 -1
  56. machineconfig/settings/shells/nushell/config.nu +2 -2
  57. machineconfig/settings/shells/nushell/env.nu +45 -6
  58. machineconfig/settings/shells/nushell/init.nu +282 -95
  59. machineconfig/settings/shells/pwsh/init.ps1 +1 -0
  60. machineconfig/settings/yazi/init.lua +4 -0
  61. machineconfig/settings/yazi/keymap_linux.toml +11 -4
  62. machineconfig/settings/yazi/theme.toml +4 -0
  63. machineconfig/settings/yazi/yazi_linux.toml +84 -0
  64. machineconfig/settings/yazi/yazi_windows.toml +58 -0
  65. machineconfig/setup_linux/web_shortcuts/interactive.sh +10 -10
  66. machineconfig/setup_windows/uv.ps1 +8 -1
  67. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +10 -10
  68. machineconfig/setup_windows/web_shortcuts/quick_init.ps1 +3 -2
  69. machineconfig/utils/accessories.py +7 -4
  70. machineconfig/utils/code.py +4 -2
  71. machineconfig/utils/installer_utils/github_release_bulk.py +104 -62
  72. machineconfig/utils/installer_utils/install_from_url.py +200 -0
  73. machineconfig/utils/installer_utils/installer_class.py +25 -74
  74. machineconfig/utils/installer_utils/installer_cli.py +40 -50
  75. machineconfig/utils/installer_utils/installer_helper.py +100 -0
  76. machineconfig/utils/installer_utils/installer_runner.py +5 -8
  77. machineconfig/utils/links.py +2 -2
  78. machineconfig/utils/meta.py +2 -2
  79. machineconfig/utils/options.py +3 -3
  80. machineconfig/utils/path_extended.py +1 -1
  81. machineconfig/utils/path_helper.py +0 -1
  82. machineconfig/utils/ssh.py +143 -409
  83. machineconfig/utils/ssh_utils/abc.py +8 -0
  84. machineconfig/utils/ssh_utils/copy_from_here.py +110 -0
  85. machineconfig/utils/ssh_utils/copy_to_here.py +302 -0
  86. machineconfig/utils/ssh_utils/utils.py +141 -0
  87. machineconfig/utils/ssh_utils/wsl.py +210 -0
  88. machineconfig/utils/upgrade_packages.py +2 -1
  89. machineconfig/utils/ve.py +11 -4
  90. {machineconfig-7.64.dist-info → machineconfig-7.83.dist-info}/METADATA +2 -2
  91. {machineconfig-7.64.dist-info → machineconfig-7.83.dist-info}/RECORD +96 -89
  92. {machineconfig-7.64.dist-info → machineconfig-7.83.dist-info}/entry_points.txt +2 -2
  93. machineconfig/scripts/python/explore.py +0 -49
  94. machineconfig/scripts/python/helpers_msearch/scripts_linux/fzfag +0 -17
  95. machineconfig/scripts/python/helpers_msearch/scripts_linux/fzfrga +0 -21
  96. machineconfig/scripts/python/helpers_msearch/scripts_linux/skrg +0 -4
  97. machineconfig/scripts/python/helpers_msearch/scripts_windows/fzfb.ps1 +0 -3
  98. machineconfig/scripts/python/helpers_msearch/scripts_windows/fzfrga.bat +0 -20
  99. machineconfig/settings/yazi/yazi.toml +0 -17
  100. machineconfig/setup_linux/others/cli_installation.sh +0 -137
  101. /machineconfig/{settings/shells/pwsh/profile.ps1 → scripts/python/helpers_fire_command/f.py} +0 -0
  102. /machineconfig/scripts/{Restore-ThunderbirdProfile.ps1 → windows/mounts/Restore-ThunderbirdProfile.ps1} +0 -0
  103. {machineconfig-7.64.dist-info → machineconfig-7.83.dist-info}/WHEEL +0 -0
  104. {machineconfig-7.64.dist-info → machineconfig-7.83.dist-info}/top_level.txt +0 -0
@@ -5,7 +5,9 @@ from collections import defaultdict
5
5
  from datetime import datetime
6
6
 
7
7
  from pathlib import Path
8
+ from rich.console import Console
8
9
  from rich.progress import track
10
+ from rich.table import Table
9
11
  import typer
10
12
 
11
13
 
@@ -14,9 +16,6 @@ if TYPE_CHECKING:
14
16
  import polars as pl
15
17
 
16
18
 
17
- app = typer.Typer()
18
-
19
-
20
19
  def count_lines_in_commit(commit: "Any") -> int:
21
20
  _total_lines = 0
22
21
  for _file in commit.stats.files:
@@ -81,7 +80,10 @@ def get_default_branch(repo: Repo) -> str:
81
80
  return repo.head.reference.name # If neither exists, get the branch the HEAD is pointing to
82
81
 
83
82
 
84
- @app.command()
83
+
84
+ console = Console()
85
+
86
+
85
87
  def count_historical(repo_path: Annotated[str, typer.Argument(..., help="Path to the git repository")]):
86
88
  """Count total historical lines of Python code in the repository."""
87
89
  print(f"Analyzing repository: {repo_path}")
@@ -89,7 +91,7 @@ def count_historical(repo_path: Annotated[str, typer.Argument(..., help="Path to
89
91
  print(f"\nTotal historical lines of Python code: {total_loc}")
90
92
 
91
93
 
92
- @app.command()
94
+
93
95
  def analyze_over_time(repo_path: Annotated[str, typer.Argument(..., help="Path to the git repository")]):
94
96
  """Analyze a git repository to track Python code size over time with visualization."""
95
97
  repo: Repo = Repo(repo_path)
@@ -170,8 +172,14 @@ def analyze_over_time(repo_path: Annotated[str, typer.Argument(..., help="Path t
170
172
  html_path = plot_dir.joinpath("code_size_evolution.html")
171
173
  png_path = plot_dir.joinpath("code_size_evolution.png")
172
174
 
173
- fig.write_html(html_path, include_plotlyjs="cdn")
174
- fig.write_image(png_path, width=1200, height=700, scale=2)
175
+ try:
176
+ fig.write_html(html_path, include_plotlyjs="cdn")
177
+ except Exception as e:
178
+ print(f"❌ Error saving HTML plot: {str(e)}")
179
+ try:
180
+ fig.write_image(png_path, width=1200, height=700, scale=2)
181
+ except Exception as e:
182
+ print(f"❌ Error saving PNG plot: {str(e)}")
175
183
 
176
184
  print(f"🖼️ Interactive plot saved as {html_path}")
177
185
  print(f"🖼️ Static image saved as {png_path}")
@@ -232,8 +240,17 @@ def _print_python_files_by_size_impl(repo_path: str) -> "Union[pl.DataFrame, Exc
232
240
  file_count: int = len(df)
233
241
 
234
242
  # Print the DataFrame
235
- print("\n📊 Python Files Line Count (sorted max to min):")
236
- print(df)
243
+ console.print("\n📊 Python Files Line Count (sorted max to min):")
244
+
245
+ table = Table(show_header=True, header_style="bold cyan")
246
+ table.add_column("#", justify="right")
247
+ table.add_column("File", overflow="fold")
248
+ table.add_column("Lines", justify="right")
249
+
250
+ for idx, row in enumerate(df.iter_rows(named=True), 1):
251
+ table.add_row(str(idx), row["filename"], f"{row['lines']:,}")
252
+
253
+ console.print(table)
237
254
  print(f"\n📁 Total Python files: {file_count}")
238
255
  print(f"📝 Total lines of Python code: {total_lines:,}")
239
256
 
@@ -335,7 +352,7 @@ def _print_python_files_by_size_impl(repo_path: str) -> "Union[pl.DataFrame, Exc
335
352
  return Exception(f"❌ Error analyzing repository: {str(e)}")
336
353
 
337
354
 
338
- @app.command()
355
+
339
356
  def print_python_files_by_size(repo_path: Annotated[str, typer.Argument(..., help="Path to the git repository")]):
340
357
  """Print Python files sorted by size with visualizations."""
341
358
  result = _print_python_files_by_size_impl(repo_path)
@@ -344,5 +361,17 @@ def print_python_files_by_size(repo_path: Annotated[str, typer.Argument(..., hel
344
361
  return
345
362
 
346
363
 
364
+ def get_app():
365
+ app = typer.Typer()
366
+ app.command(name="count-historical", no_args_is_help=True, help="[c] Count lines of code in a git repository")(count_historical)
367
+ app.command(name="c", hidden=True, no_args_is_help=True, help="Alias for count-historical")(count_historical)
368
+ app.command(name="analyze-over-time", no_args_is_help=True, help="[a] Analyze code size over time in a git repository")(analyze_over_time)
369
+ app.command(name="a", hidden=True, no_args_is_help=True, help="Alias for analyze-over-time")(analyze_over_time)
370
+ app.command(name="print-python-files-by-size", no_args_is_help=True, help="[p] Print Python files by size in a git repository")(print_python_files_by_size)
371
+ app.command(name="p", hidden=True, no_args_is_help=True, help="Alias for print-python-files-by-size")(print_python_files_by_size)
372
+ return app
373
+
374
+
347
375
  if __name__ == "__main__":
348
- app()
376
+ get_app()()
377
+
@@ -8,7 +8,7 @@ def analyze_repo_development(repo_path: Annotated[str, typer.Argument(..., help=
8
8
  from pathlib import Path
9
9
  count_lines_path = Path(count_lines.__file__)
10
10
  # --project $HOME/code/ machineconfig --group plot
11
- cmd = f"""uv run --python 3.14 --with "machineconfig[plot]>=7.64" {count_lines_path} analyze-over-time {repo_path}"""
11
+ cmd = f"""uv run --python 3.14 --with "machineconfig[plot]>=7.83" {count_lines_path} analyze-over-time {repo_path}"""
12
12
  from machineconfig.utils.code import run_shell_script
13
13
  run_shell_script(cmd)
14
14
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  from typing import Annotated, Optional
4
4
  import typer
5
+ from pathlib import Path
5
6
 
6
7
 
7
8
  def download(
@@ -12,11 +13,10 @@ def download(
12
13
  ) -> Optional["Path"]:
13
14
 
14
15
  import subprocess
15
- from pathlib import Path
16
16
  from urllib.parse import parse_qs, unquote, urlparse
17
17
  from requests import Response
18
18
  import requests
19
-
19
+ from pathlib import Path
20
20
  if url is None:
21
21
  typer.echo("❌ Error: URL is required.", err=True)
22
22
  return None
@@ -147,5 +147,6 @@ def download(
147
147
 
148
148
  return result_path.resolve()
149
149
 
150
+
150
151
  if __name__ == "__main__":
151
- from pathlib import Path
152
+ pass
@@ -2,58 +2,92 @@
2
2
 
3
3
 
4
4
  import typer
5
+ from typing import Optional, Annotated, Literal, TypedDict
5
6
 
6
- from typing import Optional
7
- from pathlib import Path
8
- from typing import Annotated, Literal, TypedDict
9
7
 
10
-
11
- def path():
8
+ def tui_env(which: Annotated[Literal["PATH", "p", "ENV", "e"], typer.Argument(help="Which environment variable to display.")] = "ENV") -> None:
12
9
  """📚 NAVIGATE PATH variable with TUI"""
13
10
  from machineconfig.scripts.python import env_manager as navigator
14
11
  from pathlib import Path
15
- path = Path(navigator.__file__).resolve().parent.joinpath("path_manager_tui.py")
12
+ match which:
13
+ case "PATH" | "p":
14
+ path = Path(navigator.__file__).resolve().parent.joinpath("path_manager_tui.py")
15
+ case "ENV" | "e":
16
+ path = Path(navigator.__file__).resolve().parent.joinpath("env_manager_tui.py")
16
17
  from machineconfig.utils.code import run_shell_script, get_uv_command_executing_python_script
17
18
  uv_with = ["textual"]
18
19
  uv_project_dir = None
19
20
  if not Path.home().joinpath("code/machineconfig").exists():
20
- uv_with.append("machineconfig>=7.64")
21
+ uv_with.append("machineconfig>=7.83")
21
22
  else:
22
23
  uv_project_dir = str(Path.home().joinpath("code/machineconfig"))
23
24
  run_shell_script(get_uv_command_executing_python_script(python_script=path.read_text(encoding="utf-8"), uv_with=uv_with, uv_project_dir=uv_project_dir)[0])
24
25
 
25
26
 
26
- def init_project(python: Annotated[Literal["3.13", "3.14"], typer.Option("--python", "-p", help="Python version for the uv virtual environment.")]= "3.13") -> None:
27
- _ = python
28
- repo_root = Path.cwd()
29
- if not (repo_root / "pyproject.toml").exists():
30
- typer.echo(" Error: pyproject.toml not found.", err=True)
31
- raise typer.Exit(code=1)
32
- print("Adding group `plot` with common data science and plotting packages...")
33
- script = """
34
- uv add --group plot \
35
- # Data & computation
36
- numpy pandas polars duckdb-engine python-magic \
37
- # Plotting / visualization
38
- matplotlib plotly kaleido \
39
- # Notebooks / interactive
40
- ipython ipykernel jupyterlab nbformat marimo \
41
- # Code analysis / type checking / linting
42
- mypy pyright ruff pylint pyrefly \
43
- # Packaging / build / dev
44
- cleanpy \
45
- # CLI / debugging / utilities
46
- ipdb pudb \
47
- # Type hints for packages
48
- types-python-dateutil types-pyyaml types-requests types-tqdm \
49
- types-mysqlclient types-paramiko types-pytz types-sqlalchemy types-toml types-urllib3 \
50
-
27
+ def init_project(
28
+ name: Annotated[Optional[str], typer.Option("--name", "-n", help="Name of the project.")]= None,
29
+ tmp_directory: Annotated[bool, typer.Option("--tmp-directory/--no-tmp-directory", "-t/-nt", help="Use a temporary directory for the project initialization.")]= False,
30
+ python: Annotated[Literal["3.13", "3.14"], typer.Option("--python", "-p", help="Python version for the uv virtual environment.")]= "3.13",
31
+ packages: Annotated[Optional[str], typer.Option("--packages", "-p", help="Additional packages to include in the uv virtual environment.")]= None,
32
+ group: Annotated[Optional[str], typer.Option("--group", "-g", help="Group name for the packages.")]= "plot",
33
+ types_packages: Annotated[bool, typer.Option("--types-packages/--no-types-packages", "-T/-NT", help="Include types packages for better type hinting.")]= True,
34
+ linting_debug_packages: Annotated[bool, typer.Option("--linting-debug-packages/--no-linting-debug-packages", "-L/-NL", help="Include linting and debugging packages.")]= True,
35
+ ia_packages: Annotated[bool, typer.Option("--ia-packages/--no-ia-packages", "-I/-NI", help="Include interactive and IA packages.")]= True,
36
+ plot_packages: Annotated[bool, typer.Option("--plot-packages/--no-plot-packages", "-P/-NP", help="Include plotting packages.")]= True,
37
+ data_packages: Annotated[bool, typer.Option("--data-packages/--no-data-packages", "-D/-ND", help="Include data manipulation packages.")]= True,
38
+ ) -> None:
39
+ if packages is not None:
40
+ packages_add_line = f"uv add {packages}"
41
+ else:
42
+ packages_add_line = ""
43
+ from pathlib import Path
44
+ if not tmp_directory:
45
+ repo_root = Path.cwd()
46
+ if not (repo_root / "pyproject.toml").exists():
47
+ typer.echo("❌ Error: pyproject.toml not found.", err=True)
48
+ raise typer.Exit(code=1)
49
+ starting_code = ""
50
+ else:
51
+ if name is not None:
52
+ from machineconfig.utils.accessories import randstr
53
+ repo_root = Path.home().joinpath(f"tmp_results/tmp_projects/{name}")
54
+ else:
55
+ from machineconfig.utils.accessories import randstr
56
+ repo_root = Path.home().joinpath(f"tmp_results/tmp_projects/{randstr(6)}")
57
+ repo_root.mkdir(parents=True, exist_ok=True)
58
+ print(f"Using temporary directory for project initialization: {repo_root}")
59
+ starting_code = f"""
60
+ cd {repo_root}
61
+ uv init --python {python}
62
+ uv venv
63
+ """
64
+ print(f"Adding group `{group}` with common data science and plotting packages...")
65
+ total_packages: list[str] = []
66
+
67
+ if types_packages:
68
+ total_packages.append("types-python-dateutil types-pyyaml types-requests types-tqdm types-mysqlclient types-paramiko types-pytz types-sqlalchemy types-toml types-urllib3")
69
+ if linting_debug_packages:
70
+ total_packages.append("mypy pyright ruff pylint pyrefly cleanpy ipdb pudb")
71
+ if ia_packages:
72
+ total_packages.append("ipython ipykernel jupyterlab nbformat marimo")
73
+ if plot_packages:
74
+ total_packages.append("python-magic matplotlib plotly kaleido")
75
+ if data_packages:
76
+ total_packages.append("numpy pandas polars duckdb-engine sqlalchemy psycopg2-binary pyarrow tqdm openpyxl")
77
+ from machineconfig.utils.ve import get_ve_activate_line
78
+ script = f"""
79
+ {starting_code}
80
+ {packages_add_line}
81
+ uv add --group {group} {" ".join(total_packages)}
82
+ {get_ve_activate_line(ve_root=str(repo_root.joinpath(".venv")))}
83
+ ls
51
84
  """
52
- from machineconfig.utils.code import run_shell_script
53
- run_shell_script(script)
85
+ from machineconfig.utils.code import exit_then_run_shell_script
86
+ exit_then_run_shell_script(script)
54
87
 
55
88
 
56
89
  def edit_file_with_hx(path: Annotated[Optional[str], typer.Argument(..., help="The root directory of the project to edit, or a file path.")] = None) -> None:
90
+ from pathlib import Path
57
91
  if path is None:
58
92
  root_path = Path.cwd()
59
93
  print(f"No path provided. Using current working directory: {root_path}")
@@ -82,6 +116,13 @@ class MachineSpecs(TypedDict):
82
116
  system: str
83
117
  distro: str
84
118
  home_dir: str
119
+ hostname: str
120
+ release: str
121
+ version: str
122
+ machine: str
123
+ processor: str
124
+ python_version: str
125
+ user: str
85
126
 
86
127
 
87
128
  def get_machine_specs() -> MachineSpecs:
@@ -90,11 +131,21 @@ def get_machine_specs() -> MachineSpecs:
90
131
  UV_RUN_CMD = "$HOME/.local/bin/uv run" if platform.system() != "Windows" else """& "$env:USERPROFILE/.local/bin/uv" run"""
91
132
  command = f"""{UV_RUN_CMD} --with distro python -c "import distro; print(distro.name(pretty=True))" """
92
133
  import subprocess
134
+ from pathlib import Path
135
+ import socket
136
+ import os
93
137
  distro = subprocess.run(command, shell=True, capture_output=True, text=True).stdout.strip()
94
138
  specs: MachineSpecs = {
95
139
  "system": platform.system(),
96
140
  "distro": distro,
97
141
  "home_dir": str(Path.home()),
142
+ "hostname": socket.gethostname(),
143
+ "release": platform.release(),
144
+ "version": platform.version(),
145
+ "machine": platform.machine(),
146
+ "processor": platform.processor() or "Unknown",
147
+ "python_version": platform.python_version(),
148
+ "user": os.getenv("USER") or os.getenv("USERNAME") or "Unknown",
98
149
  }
99
150
  print(specs)
100
151
  from machineconfig.utils.source_of_truth import CONFIG_ROOT
@@ -104,3 +155,5 @@ def get_machine_specs() -> MachineSpecs:
104
155
  path.write_text(json.dumps(specs, indent=4), encoding="utf-8")
105
156
  return specs
106
157
 
158
+ if __name__ == "__main__":
159
+ get_machine_specs()
@@ -111,7 +111,7 @@ def execute_installations(selected_options: list[str]) -> None:
111
111
  console.print(Panel("⚡ [bold bright_yellow]CLI APPLICATIONS[/bold bright_yellow]\n[italic]Command-line tools installation[/italic]", border_style="bright_yellow"))
112
112
  console.print("🔧 Installing CLI applications", style="bold cyan")
113
113
  try:
114
- from machineconfig.utils.installer_utils.installer_cli import main as devops_devapps_install_main
114
+ from machineconfig.utils.installer_utils.installer_cli import main_installer_cli as devops_devapps_install_main
115
115
  devops_devapps_install_main(group=True, which=maybe_a_group, interactive=False)
116
116
  console.print("✅ CLI applications installed successfully", style="bold green")
117
117
  except Exception as e:
@@ -57,3 +57,7 @@ def get_app():
57
57
  def main():
58
58
  app = get_app()
59
59
  app()
60
+
61
+
62
+ if __name__ == "__main__":
63
+ main()
@@ -1,21 +1,70 @@
1
1
 
2
2
  import typer
3
+ from typing import Annotated
3
4
 
4
5
 
5
- def machineconfig_find():
6
+ def machineconfig_search(
7
+ directory: Annotated[str, typer.Option(..., "--directory", "-d", help="The directory to search")] = ".",
8
+ ast: Annotated[bool, typer.Option(..., "--ast", "-a", help="The abstract syntax tree search/ tree sitter search of symbols")] = False,
9
+ symantic: Annotated[bool, typer.Option(..., "--symantic", "-s", help="The symantic search of symbols")] = False,
10
+ extension: Annotated[str, typer.Option(..., "--extension", "-e", help="File extension to filter by (e.g., .py, .js)")] = "",
11
+ file: Annotated[bool, typer.Option(..., "--file", "-f", help="File search using fzf")] = False,
12
+ no_dotfiles: Annotated[bool, typer.Option(..., "--no-dotfiles", "-d", help="Exclude dotfiles from search")] = False,
13
+ rga: Annotated[bool, typer.Option(..., "--rga", "-a", help="Use ripgrep-all for searching instead of ripgrep")] = False,
14
+ install_dependencies: Annotated[bool, typer.Option(..., "--install-dependencies", "-p", help="Install required dependencies if missing")] = False
15
+ ):
16
+ if install_dependencies:
17
+ from machineconfig.utils.installer_utils.installer_cli import install_if_missing
18
+ install_if_missing("fzf")
19
+ install_if_missing("bat")
20
+ install_if_missing("fd")
21
+ install_if_missing("rg") # ripgrep
22
+ install_if_missing("rga") # ripgrep-all
23
+ # install_if_missing("tree-sitter-cli")
24
+ return
25
+ if symantic:
26
+ script = ""
27
+ for an_ex in extension.split(","):
28
+ script = script + f"""\nparse *.{an_ex} """
29
+ from machineconfig.utils.code import run_shell_script
30
+ run_shell_script(script=script)
31
+ return
32
+ if ast:
33
+ from machineconfig.scripts.python.helpers.ast_search import get_repo_symbols
34
+ symbols = get_repo_symbols(directory)
35
+ from machineconfig.utils.options import choose_from_options
36
+ try:
37
+ res = choose_from_options(options=symbols, msg="Select a symbol to search for:", fzf=True, multi=False)
38
+ from rich import print_json
39
+ import json
40
+ res_json = json.dumps(res, indent=4)
41
+ print_json(res_json)
42
+ return None
43
+ except Exception as e:
44
+ print(f"❌ Error during selection: {e}")
45
+ return None
46
+ if file:
47
+ script = """fzf --ansi --preview-window 'right:60%' --preview 'bat --color=always --style=numbers,grid,header --line-range :300 {}' """
48
+ if no_dotfiles:
49
+ script = "fd | " + script
50
+ from machineconfig.utils.code import run_shell_script
51
+ run_shell_script(script=script)
52
+ return
6
53
  from machineconfig.scripts.python.helpers_msearch import FZFG_LINUX_PATH, FZFG_WINDOWS_PATH
7
54
  import platform
8
- if platform.system() == "Linux":
9
- script_path = FZFG_LINUX_PATH
55
+ if platform.system() == "Linux" or platform.system() == "Darwin":
56
+ script = FZFG_LINUX_PATH.read_text(encoding="utf-8")
10
57
  elif platform.system() == "Windows":
11
- script_path = FZFG_WINDOWS_PATH
58
+ script = FZFG_WINDOWS_PATH.read_text(encoding="utf-8")
12
59
  else:
13
60
  raise RuntimeError("Unsupported platform")
61
+ if rga:
62
+ script = script.replace("rg ", "rga ").replace("ripgrep", "ripgrep-all")
14
63
  from machineconfig.utils.code import exit_then_run_shell_script
15
- exit_then_run_shell_script(script=script_path.read_text(encoding="utf-8"), strict=False)
64
+ exit_then_run_shell_script(script=script, strict=False)
16
65
 
17
66
 
18
67
  def main():
19
68
  app = typer.Typer(add_completion=False, no_args_is_help=True)
20
- app.command(name="msearch", help="machineconfig search helper", no_args_is_help=False)(machineconfig_find)
69
+ app.command(name="msearch", help="machineconfig search helper", no_args_is_help=False)(machineconfig_search)
21
70
  app()
@@ -0,0 +1,132 @@
1
+
2
+ from typing import cast, Optional
3
+
4
+
5
+ def get_all_ipv4_addresses() -> list[tuple[str, str]]:
6
+ import psutil
7
+ import socket
8
+ result: list[tuple[str, str]] = []
9
+ for iface, addrs in psutil.net_if_addrs().items():
10
+ for addr in addrs:
11
+ if addr.family == socket.AF_INET:
12
+ ip = addr.address
13
+ result.append((iface, ip))
14
+ return result
15
+
16
+
17
+ def select_lan_ipv4(prefer_vpn: bool) -> Optional[str]:
18
+ """
19
+ Choose the best 'real LAN' IPv4:
20
+ - Excludes loopback/link-local and (by default) VPN/tunnel/container ifaces
21
+ - Prefers physical-looking ifaces (eth/en*/wlan/wl*)
22
+ - Prefers RFC1918 LANs: 192.168/16 > 10/8 > 172.16/12
23
+ - Requires interface is UP
24
+ Set prefer_vpn=True to allow tunnel/VPN ifaces to compete.
25
+ """
26
+
27
+ import ipaddress
28
+ import re
29
+ from collections.abc import Sequence
30
+ import psutil
31
+
32
+ # Down-rank or exclude: tunnels/VPNs/bridges/containers (add your own if needed)
33
+ VIRTUAL_IFACE_PAT = re.compile(
34
+ r"^(?:lo|loopback|docker\d*|br-.*|veth.*|virbr.*|bridge.*|"
35
+ r"vboxnet.*|vmnet.*|zt.*|ham.*|tailscale.*|wg\d*|utun\d*|llw\d*|awdl\d*|"
36
+ r"tun\d*|tap\d*|cloudflarewarp.*|warp.*)$",
37
+ re.IGNORECASE,
38
+ )
39
+
40
+ # Light preference for names that look like real NICs
41
+ PHYSICAL_IFACE_PAT = re.compile(
42
+ r"^(?:eth\d*|en\d*|enp.*|ens.*|eno.*|wlan\d*|wl.*|.*wifi.*|.*ethernet.*)$",
43
+ re.IGNORECASE,
44
+ )
45
+
46
+ # Known noisy CIDRs to avoid
47
+ NOISY_NETS: list[ipaddress.IPv4Network] = [
48
+ ipaddress.IPv4Network("100.64.0.0/10"), # CGNAT (Tailscale/others)
49
+ ipaddress.IPv4Network("172.17.0.0/16"), # docker0 default
50
+ ipaddress.IPv4Network("172.18.0.0/16"),
51
+ ipaddress.IPv4Network("172.19.0.0/16"),
52
+ ipaddress.IPv4Network("192.168.49.0/24"), # minikube default
53
+ ipaddress.IPv4Network("10.0.2.0/24"), # VirtualBox NAT
54
+ ]
55
+
56
+ def _in_any(ip: ipaddress.IPv4Address, nets: Sequence[ipaddress.IPv4Network]) -> bool:
57
+ return any(ip in n for n in nets)
58
+
59
+ stats = psutil.net_if_stats()
60
+ best = None
61
+ best_score = -10**9
62
+ import socket
63
+ for iface, addrs in psutil.net_if_addrs().items():
64
+ st = stats.get(iface)
65
+ if not st or not st.isup:
66
+ continue
67
+
68
+ for a in addrs:
69
+ if a.family != socket.AF_INET or not a.address:
70
+ continue
71
+
72
+ ip_str = a.address
73
+ try:
74
+ ip = cast(ipaddress.IPv4Address, ipaddress.ip_address(ip_str))
75
+ except ValueError:
76
+ continue
77
+
78
+ # Exclude unusable classes
79
+ if ip.is_loopback or ip.is_link_local: # 127.0.0.0/8, 169.254.0.0/16
80
+ continue
81
+
82
+ # Hard filter: if it looks virtual and we don't prefer VPNs, skip it
83
+ if not prefer_vpn and VIRTUAL_IFACE_PAT.match(iface):
84
+ continue
85
+
86
+ # Hard filter: known noisy subnets (docker, cgnat, etc.)
87
+ if _in_any(ip, NOISY_NETS) and not prefer_vpn:
88
+ continue
89
+
90
+ # Base score
91
+ score = 0
92
+
93
+ # Prefer physical-looking names
94
+ if PHYSICAL_IFACE_PAT.match(iface):
95
+ score += 200
96
+
97
+ # Broadcast present usually means L2 LAN (not point-to-point)
98
+ # (psutil puts it on the same entry as .broadcast)
99
+ if getattr(a, "broadcast", None):
100
+ score += 100
101
+
102
+ # Prefer private RFC1918; rank families
103
+ if ip.is_private:
104
+ # Order: 192.168.x.x > 10.x.x.x > 172.16-31.x.x
105
+ ip_net = ipaddress.IPv4Network((ip, 32), strict=False)
106
+ if ipaddress.IPv4Network("192.168.0.0/16").supernet_of(ip_net):
107
+ score += 90
108
+ elif ipaddress.IPv4Network("10.0.0.0/8").supernet_of(ip_net):
109
+ score += 70
110
+ elif ipaddress.IPv4Network("172.16.0.0/12").supernet_of(ip_net):
111
+ score += 50
112
+ else:
113
+ # Public on a NIC is unusual for a home/office LAN
114
+ score -= 50
115
+
116
+ # Slight nudge by interface speed if known (>0 means psutil knows it)
117
+ # (Many tunnels report 0)
118
+ if getattr(st, "speed", 0) > 0:
119
+ score += 20
120
+
121
+ # Deterministic tie-breaker: prefer shorter iface name (eth0 over eth10)
122
+ score -= len(iface) * 0.01
123
+
124
+ if score > best_score:
125
+ best_score = score
126
+ best = ip_str
127
+
128
+ return best
129
+
130
+
131
+ if __name__ == "__main__":
132
+ print(select_lan_ipv4(False) or "No LAN IPv4 found")
@@ -137,11 +137,14 @@ def main(pub_path: Annotated[Optional[str], typer.Argument(..., help="Path to th
137
137
  console.print(Panel("🚀 SSH KEY AUTHORIZATION READY\nRun the generated script to apply changes", box=box.DOUBLE_EDGE, title_align="left"))
138
138
  from machineconfig.utils.code import run_shell_script
139
139
  run_shell_script(script=program)
140
- import socket
141
- s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
142
- s.connect(('8.8.8.8', 80))
143
- local_ip_v4 = s.getsockname()[0]
144
- s.close()
140
+
141
+ import machineconfig.scripts.python.nw.address as helper
142
+ res = helper.select_lan_ipv4(prefer_vpn=False)
143
+ if res is None:
144
+ console.print(Panel("❌ ERROR: Could not determine local LAN IPv4 address", title="[bold red]Error[/bold red]", border_style="red"))
145
+ raise typer.Exit(code=1)
146
+ local_ip_v4 = res
147
+
145
148
  console.print(Panel(f"🌐 This computer is accessible at: {local_ip_v4}", title="[bold green]Network Info[/bold green]", border_style="green"))
146
149
  console.print(Panel("✅ SSH KEY AUTHORIZATION COMPLETED", box=box.DOUBLE_EDGE, title_align="left"))
147
150
 
@@ -1,5 +1,3 @@
1
- import subprocess
2
- # from machineconfig.utils.schemas.layouts.layout_types import LayoutConfig
3
1
  import typer
4
2
  from typing import Annotated
5
3
 
@@ -26,6 +24,7 @@ def choose_zellij_session(
26
24
  typer.Exit()
27
25
  return
28
26
  cmd = "zellij list-sessions"
27
+ import subprocess
29
28
  sessions: list[str] = subprocess.check_output(cmd, shell=True).decode().strip().split("\n")
30
29
  # filter out empty lines and keep raw lines (they contain creation info)
31
30
  sessions = [s for s in sessions if s.strip()]
@@ -63,6 +62,7 @@ def choose_zellij_session(
63
62
 
64
63
  def get_session_tabs() -> list[tuple[str, str]]:
65
64
  cmd = "zellij list-sessions"
65
+ import subprocess
66
66
  sessions: list[str] = subprocess.check_output(cmd, shell=True).decode().strip().split("\n")
67
67
  sessions = [strip_ansi_codes(s) for s in sessions]
68
68
  active_sessions = [s for s in sessions if "EXITED" not in s]
@@ -1,11 +1,12 @@
1
1
 
2
2
 
3
3
  from machineconfig.scripts.python.helpers_devops.cli_utils import merge_pdfs, compress_pdf
4
- from machineconfig.scripts.python.helpers_utils.path import edit_file_with_hx, get_machine_specs, init_project, path
4
+ from machineconfig.scripts.python.helpers_utils.path import edit_file_with_hx, get_machine_specs, init_project, tui_env
5
5
  from machineconfig.scripts.python.helpers_utils.download import download
6
6
  import typer
7
7
  from typing import Annotated
8
8
 
9
+
9
10
  def kill_process(
10
11
  # name: Annotated[Optional[str], typer.Option(..., "--name", "-n", help="Name of the process to kill")],
11
12
  # command: Annotated[str, typer.Option(..., "--command", "-c", help="Match by command line instead of process name")] = "",
@@ -29,27 +30,27 @@ def upgrade_packages():
29
30
 
30
31
  def get_app() -> typer.Typer:
31
32
  app = typer.Typer(help="🛠️ utilities operations", no_args_is_help=True, add_help_option=False, add_completion=False)
32
- app.command(name="kill-process", no_args_is_help=False, help="[k] Choose a process to kill")(kill_process)
33
+ app.command(name="kill-process", no_args_is_help=False, help="⚔️ [k] Choose a process to kill")(kill_process)
33
34
  app.command(name="k", no_args_is_help=False, help="Choose a process to kill", hidden=True)(kill_process)
34
35
 
35
- app.command("path", no_args_is_help=False, help="📚 [p] NAVIGATE PATH variable with TUI")(path)
36
- app.command("p", no_args_is_help=False, help="NAVIGATE PATH variable with TUI", hidden=True)(path)
36
+ app.command("environment", no_args_is_help=False, help="📚 [v] NAVIGATE ENV/PATH variable with TUI")(tui_env)
37
+ app.command("v", no_args_is_help=False, help="NAVIGATE ENV/PATH variable with TUI", hidden=True)(tui_env)
37
38
 
38
- app.command(name="upgrade-packages", no_args_is_help=False, help="[up] Upgrade project dependencies.")(upgrade_packages)
39
+ app.command(name="upgrade-packages", no_args_is_help=False, help="⬆️ [up] Upgrade project dependencies.")(upgrade_packages)
39
40
  app.command(name="up", no_args_is_help=False, hidden=True)(upgrade_packages)
40
41
 
41
- app.command(name="download", no_args_is_help=True, help="[d] Download a file from a URL and optionally decompress it.")(download)
42
+ app.command(name="download", no_args_is_help=True, help="⬇️ [d] Download a file from a URL and optionally decompress it.")(download)
42
43
  app.command(name="d", no_args_is_help=True, hidden=True)(download)
43
- app.command(name="get-machine-specs", no_args_is_help=False, help="[g] Get machine specifications.")(get_machine_specs)
44
+ app.command(name="get-machine-specs", no_args_is_help=False, help="💻 [g] Get machine specifications.")(get_machine_specs)
44
45
  app.command(name="g", no_args_is_help=False, hidden=True)(get_machine_specs)
45
- app.command(name="init-project", no_args_is_help=False, help="[i] Initialize a project with a uv virtual environment and install dev packages.")(init_project)
46
+ app.command(name="init-project", no_args_is_help=False, help="🚀 [i] Initialize a project with a uv virtual environment and install dev packages.")(init_project)
46
47
  app.command(name="i", no_args_is_help=False, hidden=True)(init_project)
47
- app.command(name="edit", no_args_is_help=False, help="[e] Open a file in the default editor.")(edit_file_with_hx)
48
+ app.command(name="edit", no_args_is_help=False, help="✏️ [e] Open a file in the default editor.")(edit_file_with_hx)
48
49
  app.command(name="e", no_args_is_help=False, hidden=True)(edit_file_with_hx)
49
50
 
50
- app.command(name="pdf-merge", no_args_is_help=True, help="[pm] Merge two PDF files into one.")(merge_pdfs)
51
+ app.command(name="pdf-merge", no_args_is_help=True, help="📄 [pm] Merge two PDF files into one.")(merge_pdfs)
51
52
  app.command(name="pm", no_args_is_help=True, hidden=True)(merge_pdfs)
52
- app.command(name="pdf-compress", no_args_is_help=True, help="[pc] Compress a PDF file.")(compress_pdf)
53
+ app.command(name="pdf-compress", no_args_is_help=True, help="📦 [pc] Compress a PDF file.")(compress_pdf)
53
54
  app.command(name="pc", no_args_is_help=True, hidden=True)(compress_pdf)
54
55
 
55
56
  # app.command(name="copy", no_args_is_help=True, help="[c] Copy files or directories.")(copy)
@@ -7,7 +7,7 @@ $user = ''
7
7
  $sharePath = ''
8
8
  $driveLetter = ''
9
9
 
10
- uv run --python 3.14 --with "machineconfig>=7.64" python -m machineconfig.scripts.python.mount_ssh
10
+ uv run --python 3.14 --with "machineconfig>=7.83" python -m machineconfig.scripts.python.mount_ssh
11
11
 
12
12
  net use T: \\sshfs.kr\$user@$host.local
13
13
  # this worked: net use T: \\sshfs\alex@alex-p51s-5.local