machineconfig 7.53__py3-none-any.whl → 7.69__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 (90) hide show
  1. machineconfig/cluster/sessions_managers/utils/maker.py +21 -9
  2. machineconfig/jobs/installer/custom/boxes.py +2 -2
  3. machineconfig/jobs/installer/custom/hx.py +15 -12
  4. machineconfig/jobs/installer/custom_dev/cloudflare_warp_cli.py +23 -0
  5. machineconfig/jobs/installer/custom_dev/dubdb_adbc.py +1 -1
  6. machineconfig/jobs/installer/custom_dev/nerfont_windows_helper.py +1 -1
  7. machineconfig/jobs/installer/custom_dev/sysabc.py +39 -34
  8. machineconfig/jobs/installer/custom_dev/wezterm.py +0 -4
  9. machineconfig/jobs/installer/installer_data.json +103 -35
  10. machineconfig/jobs/installer/package_groups.py +28 -13
  11. machineconfig/scripts/__init__.py +0 -4
  12. machineconfig/scripts/linux/wrap_mcfg +1 -1
  13. machineconfig/scripts/python/ai/solutions/copilot/instructions/python/dev.instructions.md +3 -0
  14. machineconfig/scripts/python/croshell.py +22 -17
  15. machineconfig/scripts/python/devops.py +3 -4
  16. machineconfig/scripts/python/devops_navigator.py +0 -4
  17. machineconfig/scripts/python/env_manager/path_manager_tui.py +1 -1
  18. machineconfig/scripts/python/fire_jobs.py +17 -15
  19. machineconfig/scripts/python/ftpx.py +13 -11
  20. machineconfig/scripts/python/helpers/ast_search.py +74 -0
  21. machineconfig/scripts/python/helpers/repo_rag.py +325 -0
  22. machineconfig/scripts/python/helpers/symantic_search.py +25 -0
  23. machineconfig/scripts/python/helpers_cloud/cloud_copy.py +28 -21
  24. machineconfig/scripts/python/helpers_cloud/cloud_helpers.py +1 -1
  25. machineconfig/scripts/python/helpers_cloud/cloud_sync.py +8 -7
  26. machineconfig/scripts/python/helpers_croshell/crosh.py +2 -2
  27. machineconfig/scripts/python/helpers_devops/cli_config_dotfile.py +22 -13
  28. machineconfig/scripts/python/helpers_devops/cli_self.py +7 -6
  29. machineconfig/scripts/python/helpers_devops/cli_share_file.py +2 -2
  30. machineconfig/scripts/python/helpers_devops/cli_share_server.py +1 -1
  31. machineconfig/scripts/python/helpers_devops/cli_terminal.py +1 -1
  32. machineconfig/scripts/python/helpers_devops/cli_utils.py +2 -73
  33. machineconfig/scripts/python/helpers_devops/devops_backup_retrieve.py +4 -4
  34. machineconfig/scripts/python/helpers_fire_command/file_wrangler.py +2 -3
  35. machineconfig/scripts/python/helpers_fire_command/fire_jobs_route_helper.py +3 -4
  36. machineconfig/scripts/python/helpers_navigator/command_tree.py +50 -18
  37. machineconfig/scripts/python/helpers_repos/cloud_repo_sync.py +13 -5
  38. machineconfig/scripts/python/helpers_repos/count_lines_frontend.py +1 -1
  39. machineconfig/scripts/python/helpers_repos/entrypoint.py +2 -1
  40. machineconfig/scripts/python/helpers_repos/record.py +2 -1
  41. machineconfig/scripts/python/helpers_sessions/sessions_multiprocess.py +5 -5
  42. machineconfig/scripts/python/helpers_utils/download.py +152 -0
  43. machineconfig/scripts/python/helpers_utils/path.py +4 -2
  44. machineconfig/scripts/python/interactive.py +11 -14
  45. machineconfig/scripts/python/{machineconfig.py → mcfg_entry.py} +4 -0
  46. machineconfig/scripts/python/msearch.py +21 -2
  47. machineconfig/scripts/python/nw/devops_add_ssh_key.py +21 -5
  48. machineconfig/scripts/python/nw/ssh_debug_linux.py +7 -7
  49. machineconfig/scripts/python/nw/ssh_debug_windows.py +4 -4
  50. machineconfig/scripts/python/nw/wsl_windows_transfer.py +3 -2
  51. machineconfig/scripts/python/sessions.py +35 -20
  52. machineconfig/scripts/python/terminal.py +2 -2
  53. machineconfig/scripts/python/utils.py +12 -10
  54. machineconfig/scripts/windows/mounts/mount_ssh.ps1 +1 -1
  55. machineconfig/settings/lf/windows/lfcd.ps1 +1 -1
  56. machineconfig/settings/shells/pwsh/init.ps1 +1 -0
  57. machineconfig/settings/shells/wezterm/wezterm.lua +2 -0
  58. machineconfig/settings/shells/zsh/init.sh +0 -7
  59. machineconfig/settings/yazi/shell/yazi_cd.ps1 +29 -5
  60. machineconfig/setup_linux/web_shortcuts/interactive.sh +12 -11
  61. machineconfig/setup_windows/uv.ps1 +8 -1
  62. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +12 -11
  63. machineconfig/setup_windows/web_shortcuts/quick_init.ps1 +4 -2
  64. machineconfig/utils/accessories.py +7 -4
  65. machineconfig/utils/code.py +6 -4
  66. machineconfig/utils/files/headers.py +2 -2
  67. machineconfig/utils/installer_utils/install_from_url.py +180 -0
  68. machineconfig/utils/installer_utils/installer_class.py +56 -46
  69. machineconfig/utils/installer_utils/{installer.py → installer_cli.py} +71 -65
  70. machineconfig/utils/{installer.py → installer_utils/installer_runner.py} +1 -25
  71. machineconfig/utils/meta.py +28 -15
  72. machineconfig/utils/options.py +4 -4
  73. machineconfig/utils/path_extended.py +40 -19
  74. machineconfig/utils/path_helper.py +33 -31
  75. machineconfig/utils/schemas/layouts/layout_types.py +1 -1
  76. machineconfig/utils/ssh.py +330 -99
  77. machineconfig/utils/ve.py +11 -4
  78. machineconfig-7.69.dist-info/METADATA +124 -0
  79. {machineconfig-7.53.dist-info → machineconfig-7.69.dist-info}/RECORD +85 -83
  80. {machineconfig-7.53.dist-info → machineconfig-7.69.dist-info}/entry_points.txt +2 -2
  81. machineconfig/jobs/installer/linux_scripts/pgsql.sh +0 -41
  82. machineconfig/scripts/python/explore.py +0 -49
  83. machineconfig/scripts/python/nw/add_ssh_key.py +0 -148
  84. machineconfig/settings/lf/linux/exe/fzf_nano.sh +0 -16
  85. machineconfig-7.53.dist-info/METADATA +0 -94
  86. /machineconfig/jobs/installer/linux_scripts/{warp-cli.sh → cloudflare_warp_cli.sh} +0 -0
  87. /machineconfig/scripts/{Restore-ThunderbirdProfile.ps1 → windows/mounts/Restore-ThunderbirdProfile.ps1} +0 -0
  88. /machineconfig/utils/installer_utils/{installer_abc.py → installer_locator_utils.py} +0 -0
  89. {machineconfig-7.53.dist-info → machineconfig-7.69.dist-info}/WHEEL +0 -0
  90. {machineconfig-7.53.dist-info → machineconfig-7.69.dist-info}/top_level.txt +0 -0
@@ -43,3 +43,6 @@ applyTo: "**/*.py"
43
43
  * Please avoid writing README files and avoid docstring and comments in code unless absolutely necessary. Use clear naming conventions instead of documenting.
44
44
  * Always prefer to functional style of programming over OOP.
45
45
  * When passing arguments or constructing dicts or lists or tuples, avoid breaking lines too much, try to use ~ 150 characters per line before breaking to new one.
46
+
47
+ # Privacy:
48
+ * No matter what, never ever open/read/write/list anything under ~/dotfiles
@@ -16,13 +16,15 @@ def croshell(
16
16
  jupyter: Annotated[bool, typer.Option("--jupyter", "-j", help="run in jupyter interactive console")] = False,
17
17
  vscode: Annotated[bool, typer.Option("--vscode", "-c", help="open the script in vscode")] = False,
18
18
  # streamlit_viewer: Annotated[bool, typer.Option("--streamlit", "-s", help="view in streamlit app")] = False,
19
+ uv_with: Annotated[Optional[str], typer.Option("--uv-with", "-w", help="specify uv with packages to use")] = None,
19
20
  visidata: Annotated[bool, typer.Option("--visidata", "-v", help="open data file in visidata")] = False,
20
21
  marimo: Annotated[bool, typer.Option("--marimo", "-m", help="open the notebook using marimo if available")] = False,
21
22
  ) -> None:
23
+ if uv_with is not None: user_uv_with_line = f"--with {uv_with} "
24
+ else: user_uv_with_line = ""
25
+
22
26
  from machineconfig.scripts.python.helpers_croshell.crosh import get_read_python_file_pycode, get_read_data_pycode
23
27
  from machineconfig.utils.meta import lambda_to_python_script
24
- from machineconfig.utils.path_helper import get_choice_file
25
- from machineconfig.utils.path_extended import PathExtended
26
28
  from pathlib import Path
27
29
  from machineconfig.utils.accessories import randstr
28
30
  import json
@@ -37,13 +39,15 @@ def croshell(
37
39
  ipython_profile: Optional[str] = profile
38
40
  file_obj = Path.cwd() # initialization value, could be modified according to args.
39
41
  if path is not None:
42
+ from machineconfig.utils.path_helper import get_choice_file
40
43
  choice_file = get_choice_file(path=path, suffixes={".*"})
41
44
  if choice_file.suffix == ".py":
42
45
  program = choice_file.read_text(encoding="utf-8")
43
46
  text = f"📄 Selected file: {choice_file.name}"
44
47
  console.print(Panel(text, title="[bold blue]Info[/bold blue]"))
45
48
  else:
46
- program = lambda_to_python_script(lambda: get_read_data_pycode(path=str(choice_file)), in_global=True, import_module=False)
49
+ program = lambda_to_python_script(lambda: get_read_data_pycode(path=str(choice_file)),
50
+ in_global=True, import_module=False)
47
51
  text = f"📄 Reading data from: {file_obj.name}"
48
52
  console.print(Panel(text, title="[bold blue]Info[/bold blue]"))
49
53
  else: # if nothing is specified, then run in interactive mode.
@@ -64,11 +68,12 @@ def croshell(
64
68
  return textwrap.dedent("\n".join(inspect.getsource(f).splitlines()[1:]))
65
69
  preprogram += get_body_simple_function_no_args(preprogram_func)
66
70
 
67
- pyfile = PathExtended.tmp().joinpath(f"tmp_scripts/python/croshell/{randstr()}/script.py")
71
+ from pathlib import Path
72
+ pyfile = Path.home().joinpath(f"tmp_results/tmp_scripts/python/croshell/{randstr()}/script.py")
68
73
  pyfile.parent.mkdir(parents=True, exist_ok=True)
69
-
70
74
  title = "Reading Data"
71
- def_code = lambda_to_python_script(lambda: get_read_python_file_pycode(path=str(pyfile), title=title), in_global=False, import_module=False)
75
+ def_code = lambda_to_python_script(lambda: get_read_python_file_pycode(path=str(pyfile), title=title),
76
+ in_global=False, import_module=False)
72
77
  # print(def_code)
73
78
  python_program = preprogram + "\n\n" + def_code + program
74
79
  pyfile.write_text(python_program, encoding="utf-8")
@@ -102,37 +107,37 @@ def croshell(
102
107
  pass
103
108
  if visidata:
104
109
  if file_obj.suffix == ".json":
105
- fire_line = f"uv run --python 3.14 --with visidata vd {str(file_obj)}"
110
+ fire_line = f"uv run --python 3.14 {user_uv_with_line} --with visidata vd {str(file_obj)}"
106
111
  else:
107
- fire_line = f"uv run --python 3.14 --with visidata,pyarrow vd {str(file_obj)}"
112
+ fire_line = f"uv run --python 3.14 {user_uv_with_line}--with visidata,pyarrow vd {str(file_obj)}"
108
113
  elif marimo:
109
- if Path.home().joinpath("code/machineconfig").exists(): requirements = f"""--with marimo --project "{str(Path.home().joinpath("code/machineconfig"))}" """
110
- else: requirements = """--python 3.14 --with "marimo,cowsay,machineconfig[plot]>=7.53" """
114
+ if Path.home().joinpath("code/machineconfig").exists(): requirements = f"""{user_uv_with_line} --with marimo --project "{str(Path.home().joinpath("code/machineconfig"))}" """
115
+ else: requirements = f"""--python 3.14 {user_uv_with_line} user_uv_with_line--with "marimo,cowsay,machineconfig[plot]>=7.69" """
111
116
  fire_line = f"""
112
117
  cd {str(pyfile.parent)}
113
118
  uv run --python 3.14 --with "marimo" marimo convert {pyfile.name} -o marimo_nb.py
114
119
  uv run {requirements} marimo edit --host 0.0.0.0 marimo_nb.py
115
120
  """
116
121
  elif jupyter:
117
- if Path.home().joinpath("code/machineconfig").exists(): requirements = f"""--project "{str(Path.home().joinpath("code/machineconfig"))}" --with jupyterlab """
118
- else: requirements = """--with "cowsay,machineconfig[plot]>=7.53" """
122
+ if Path.home().joinpath("code/machineconfig").exists(): requirements = f"""{user_uv_with_line} --with jupyterlab --project "{str(Path.home().joinpath("code/machineconfig"))}" """
123
+ else: requirements = f"""{user_uv_with_line} --with "cowsay,machineconfig[plot]>=7.69" """
119
124
  fire_line = f"uv run {requirements} jupyter-lab {str(nb_target)}"
120
125
  elif vscode:
126
+ user_uv_add = f"uv add {uv_with}" if uv_with is not None else ""
121
127
  fire_line = f"""
122
128
  cd {str(pyfile.parent)}
123
129
  uv init --python 3.14
124
130
  uv venv
125
- uv add "cowsay,machineconfig[plot]>=7.53"
131
+ uv add "cowsay,machineconfig[plot]>=7.69"
132
+ uv add {user_uv_add}
126
133
  # code serve-web
127
134
  code --new-window {str(pyfile)}
128
135
  """
129
136
  else:
130
137
  if interpreter == "ipython": profile = f" --profile {ipython_profile} --no-banner"
131
138
  else: profile = ""
132
- if Path.home().joinpath("code/machineconfig").exists(): ve_line = f"""--project "{str(Path.home().joinpath("code/machineconfig"))}" """
133
- else: ve_line = """--python 3.14 --with "cowsay,machineconfig[plot]>=7.53" """
134
- # ve_path_maybe, ipython_profile_maybe = get_ve_path_and_ipython_profile(Path.cwd())
135
- # --python 3.14
139
+ if Path.home().joinpath("code/machineconfig").exists(): ve_line = f"""{user_uv_with_line} --project "{str(Path.home().joinpath("code/machineconfig"))}" """
140
+ else: ve_line = f"""--python 3.14 {user_uv_with_line} --with "cowsay,machineconfig[plot]>=7.69" """
136
141
  fire_line = f"uv run {ve_line} {interpreter} {interactivity} {profile} {str(pyfile)}"
137
142
 
138
143
  from machineconfig.utils.code import exit_then_run_shell_script
@@ -12,17 +12,16 @@ import machineconfig.scripts.python.helpers_devops.cli_nw as cli_network
12
12
 
13
13
  def install(which: Annotated[Optional[str], typer.Argument(..., help="Comma-separated list of program names to install, or group name if --group flag is set.")] = None,
14
14
  group: Annotated[bool, typer.Option(..., "--group", "-g", help="Treat 'which' as a group name. A group is bundle of apps.")] = False,
15
- interactive: Annotated[bool, typer.Option(..., "--interactive", "-ia", help="Interactive selection of programs to install.")] = False,
15
+ interactive: Annotated[bool, typer.Option(..., "--interactive", "-i", help="Interactive selection of programs to install.")] = False,
16
16
  ) -> None:
17
17
  """📦 Install packages"""
18
- import machineconfig.utils.installer_utils.installer as installer_entry_point
19
- installer_entry_point.main(which=which, group=group, interactive=interactive)
18
+ import machineconfig.utils.installer_utils.installer_cli as installer_entry_point
19
+ installer_entry_point.main_installer_cli(which=which, group=group, interactive=interactive)
20
20
 
21
21
 
22
22
  def get_app():
23
23
  app = typer.Typer(help="🛠️ DevOps operations", no_args_is_help=True, add_help_option=False,
24
24
  add_completion=False)
25
- _ = install
26
25
  app.command("install", no_args_is_help=True, help="🛠️ [i] Install essential packages")(install)
27
26
  app.command("i", no_args_is_help=True, help="Install essential packages", hidden=True)(install)
28
27
  app_repos = cli_repos.get_app()
@@ -1,7 +1,3 @@
1
- """
2
- TUI for navigating through machineconfig command structure using Textual.
3
- """
4
-
5
1
  from machineconfig.scripts.python.helpers_navigator import CommandNavigatorApp
6
2
 
7
3
 
@@ -2,7 +2,7 @@
2
2
  # /// script
3
3
  # requires-python = ">=3.13"
4
4
  # dependencies = [
5
- # "machineconfig>=7.53",
5
+ # "machineconfig>=7.69",
6
6
  # "textual",
7
7
  # "pyperclip",
8
8
  # ]
@@ -7,20 +7,16 @@ fire
7
7
 
8
8
  """
9
9
 
10
- from machineconfig.utils.ve import get_ve_path_and_ipython_profile
11
- from machineconfig.utils.accessories import get_repo_root, randstr
12
- from machineconfig.scripts.python.helpers_fire_command.fire_jobs_args_helper import FireJobArgs, extract_kwargs, parse_fire_args_from_context
13
- from machineconfig.utils.path_helper import get_choice_file
14
-
15
- import platform
16
10
  from typing import Optional, Annotated
17
- from pathlib import Path
18
11
  import typer
19
12
 
20
13
 
21
- def route(args: FireJobArgs, fire_args: str = "") -> None:
14
+ def route(args: "FireJobArgs", fire_args: str = "") -> None:
15
+ from pathlib import Path
16
+ from machineconfig.utils.path_helper import get_choice_file
17
+ from machineconfig.utils.accessories import get_repo_root, randstr
22
18
  choice_file = get_choice_file(args.path, suffixes=None)
23
- repo_root = get_repo_root(Path(choice_file))
19
+ repo_root = get_repo_root(choice_file)
24
20
  print(f"💾 Selected file: {choice_file}.\nRepo root: {repo_root}")
25
21
  if args.marimo:
26
22
  print(f"🧽 Preparing to launch Marimo notebook for `{choice_file}`...")
@@ -32,13 +28,13 @@ uv run --python 3.14 --with marimo marimo convert {choice_file} -o marimo_nb.py
32
28
  uv run --project {repo_root} --with marimo marimo edit --host 0.0.0.0 marimo_nb.py
33
29
  """
34
30
  from machineconfig.utils.code import exit_then_run_shell_script
35
-
36
31
  print(f"🚀 Launching Marimo notebook for `{choice_file}`...")
37
32
  exit_then_run_shell_script(script)
38
33
  return
39
34
 
40
35
  # ========================= preparing kwargs_dict
41
36
  if choice_file.suffix == ".py":
37
+ from machineconfig.scripts.python.helpers_fire_command.fire_jobs_args_helper import extract_kwargs
42
38
  kwargs_dict = extract_kwargs(args) # This now returns empty dict, but kept for compatibility
43
39
  else:
44
40
  kwargs_dict = {}
@@ -63,6 +59,7 @@ uv run --project {repo_root} --with marimo marimo edit --host 0.0.0.0 marimo_nb.
63
59
  exe = f"uv run {with_project} jupyter-lab"
64
60
  else:
65
61
  if args.interactive:
62
+ from machineconfig.utils.ve import get_ve_path_and_ipython_profile
66
63
  _ve_root_from_file, ipy_profile = get_ve_path_and_ipython_profile(choice_file)
67
64
  if ipy_profile is None:
68
65
  ipy_profile = "default"
@@ -76,9 +73,8 @@ uv run --project {repo_root} --with marimo marimo edit --host 0.0.0.0 marimo_nb.
76
73
  else:
77
74
  raise NotImplementedError(f"File type {choice_file.suffix} not supported, in the sense that I don't know how to fire it.")
78
75
 
79
- if (
80
- args.module or (args.debug and args.choose_function)
81
- ): # because debugging tools do not support choosing functions and don't interplay with fire module. So the only way to have debugging and choose function options is to import the file as a module into a new script and run the function of interest there and debug the new script.
76
+ if args.module or (args.debug and args.choose_function):
77
+ # because debugging tools do not support choosing functions and don't interplay with fire module. So the only way to have debugging and choose function options is to import the file as a module into a new script and run the function of interest there and debug the new script.
82
78
  assert choice_file.suffix == ".py", f"File must be a python file to be imported as a module. Got {choice_file}"
83
79
  from machineconfig.scripts.python.helpers_fire_command.file_wrangler import get_import_module_code, wrap_import_in_try_except
84
80
  from machineconfig.utils.meta import lambda_to_python_script
@@ -92,9 +88,12 @@ uv run --project {repo_root} --with marimo marimo edit --host 0.0.0.0 marimo_nb.
92
88
  in_global=True,
93
89
  import_module=False,
94
90
  )
91
+ # print(f"🧩 Preparing import code for module import:\n{import_code}")
95
92
  code_printing = lambda_to_python_script(
96
- lambda: print_code(code=import_code_robust, lexer="python", desc="import code"), in_global=True, import_module=False
93
+ lambda: print_code(code=import_code_robust, lexer="python", desc="import as module code"),
94
+ in_global=True, import_module=False
97
95
  )
96
+ print(f"🧩 Preparing import code for module import:\n{import_code}")
98
97
  if choice_function is not None:
99
98
  calling = f"""res = {choice_function}({("**" + str(kwargs_dict)) if kwargs_dict else ""})"""
100
99
  else:
@@ -105,6 +104,7 @@ uv run --project {repo_root} --with marimo marimo edit --host 0.0.0.0 marimo_nb.
105
104
 
106
105
  # ========================= determining basic command structure: putting together exe & choice_file & choice_function & pdb
107
106
  if args.debug:
107
+ import platform
108
108
  if platform.system() == "Windows":
109
109
  command = f"{exe} -m ipdb {choice_file} " # pudb is not available on windows machines, use poor man's debugger instead.
110
110
  elif platform.system() in ["Linux", "Darwin"]:
@@ -172,6 +172,7 @@ uv run --project {repo_root} --with marimo marimo edit --host 0.0.0.0 marimo_nb.
172
172
  export_line = add_to_path(path_variable="PYTHONPATH", directory=str(repo_root))
173
173
  command = export_line + "\n" + command
174
174
  if args.loop:
175
+ import platform
175
176
  if platform.system() in ["Linux", "Darwin"]:
176
177
  command = command + "\nsleep 0.5"
177
178
  elif platform.system() == "Windows":
@@ -213,6 +214,7 @@ def fire(
213
214
  """Main function to process fire jobs arguments."""
214
215
 
215
216
  # Get Fire arguments from context
217
+ from machineconfig.scripts.python.helpers_fire_command.fire_jobs_args_helper import FireJobArgs, parse_fire_args_from_context
216
218
  fire_args = parse_fire_args_from_context(ctx)
217
219
 
218
220
  args = FireJobArgs(
@@ -265,4 +267,4 @@ def main():
265
267
 
266
268
 
267
269
  if __name__ == "__main__":
268
- pass
270
+ from machineconfig.scripts.python.helpers_fire_command.fire_jobs_args_helper import FireJobArgs
@@ -6,17 +6,7 @@ Currently, the only way to work around this is to predifine the host in ~/.ssh/c
6
6
  """
7
7
 
8
8
  import typer
9
- from typing_extensions import Annotated
10
- from rich.console import Console
11
- from rich.panel import Panel
12
-
13
- from machineconfig.utils.ssh import SSH
14
- from machineconfig.utils.path_extended import PathExtended
15
- from machineconfig.scripts.python.helpers_cloud.helpers2 import ES
16
- from machineconfig.utils.accessories import pprint
17
-
18
-
19
- console = Console()
9
+ from typing import Annotated
20
10
 
21
11
 
22
12
  def ftpx(
@@ -26,6 +16,18 @@ def ftpx(
26
16
  zipFirst: Annotated[bool, typer.Option("--zipFirst", "-z", help="Zip before sending.")] = False,
27
17
  cloud: Annotated[bool, typer.Option("--cloud", "-c", help="Transfer through the cloud.")] = False,
28
18
  ) -> None:
19
+
20
+ from rich.console import Console
21
+ from rich.panel import Panel
22
+
23
+ from machineconfig.utils.ssh import SSH
24
+ from machineconfig.utils.path_extended import PathExtended
25
+ from machineconfig.scripts.python.helpers_cloud.helpers2 import ES
26
+ from machineconfig.utils.accessories import pprint
27
+
28
+
29
+ console = Console()
30
+
29
31
  console.print(
30
32
  Panel(
31
33
  "\n".join(
@@ -0,0 +1,74 @@
1
+
2
+ import ast
3
+ import os
4
+ from typing import TypedDict
5
+
6
+
7
+ class SymbolInfo(TypedDict):
8
+ """Represents a symbol (module, class, or function) in the repository."""
9
+ type: str
10
+ name: str
11
+ path: str
12
+ # line: int | None
13
+ # column: int | None
14
+ docstring: str
15
+
16
+
17
+ def _get_docstring(node: ast.AsyncFunctionDef | ast.FunctionDef | ast.ClassDef | ast.Module) -> str:
18
+ """Extract docstring from an AST node."""
19
+ return ast.get_docstring(node) or ""
20
+
21
+
22
+ def _extract_symbols(tree: ast.AST, module_path: str, source: str) -> list[SymbolInfo]:
23
+ """Extract symbols from an AST tree."""
24
+ symbols: list[SymbolInfo] = []
25
+
26
+ for node in ast.walk(tree):
27
+ if isinstance(node, ast.FunctionDef | ast.AsyncFunctionDef):
28
+ symbol: SymbolInfo = {
29
+ "type": "function",
30
+ "name": node.name,
31
+ "path": f"{module_path}.{node.name}",
32
+ "docstring": _get_docstring(node),
33
+ }
34
+ symbols.append(symbol)
35
+ elif isinstance(node, ast.ClassDef):
36
+ symbol: SymbolInfo = {
37
+ "type": "class",
38
+ "name": node.name,
39
+ "path": f"{module_path}.{node.name}",
40
+ "docstring": _get_docstring(node),
41
+ }
42
+ symbols.append(symbol)
43
+
44
+ return symbols
45
+
46
+
47
+ def get_repo_symbols(repo_path: str) -> list[SymbolInfo]:
48
+ skip_dirs = {'.venv', 'venv', '__pycache__', '.mypy_cache', '.pytest_cache', '.git'}
49
+ results: list[SymbolInfo] = []
50
+ counter: int = 0
51
+ for root, dirs, files in os.walk(repo_path):
52
+ dirs[:] = [d for d in dirs if d not in skip_dirs and not d.startswith('.')]
53
+ for file in files:
54
+ if not file.endswith(".py"):
55
+ continue
56
+ file_path = os.path.join(root, file)
57
+ module_path = (
58
+ os.path.relpath(file_path, repo_path)
59
+ .replace(os.sep, ".")
60
+ .removesuffix(".py")
61
+ )
62
+ try:
63
+ if counter % 100 == 0: print(f"🔍 Parsing {counter}: {file_path}...")
64
+ with open(file_path, encoding="utf-8") as f:
65
+ source = f.read()
66
+ tree = ast.parse(source, filename=file_path)
67
+ symbols = _extract_symbols(tree, module_path, source)
68
+ results.extend(symbols)
69
+ except Exception as e:
70
+ print(f"⚠️ Error parsing {file_path}: {e}")
71
+ continue
72
+ counter += 1
73
+
74
+ return results