machineconfig 3.99__py3-none-any.whl → 7.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 (418) hide show
  1. machineconfig/__init__.py +0 -28
  2. machineconfig/cluster/remote/distribute.py +0 -1
  3. machineconfig/cluster/remote/file_manager.py +0 -2
  4. machineconfig/cluster/remote/script_execution.py +1 -2
  5. machineconfig/cluster/sessions_managers/{enhanced_command_runner.py → helpers/enhanced_command_runner.py} +4 -6
  6. machineconfig/cluster/sessions_managers/helpers/load_balancer_helper.py +145 -0
  7. machineconfig/cluster/sessions_managers/utils/load_balancer.py +53 -0
  8. machineconfig/cluster/sessions_managers/utils/maker.py +69 -0
  9. machineconfig/cluster/sessions_managers/wt_local.py +128 -330
  10. machineconfig/cluster/sessions_managers/wt_local_manager.py +53 -187
  11. machineconfig/cluster/sessions_managers/wt_remote.py +51 -43
  12. machineconfig/cluster/sessions_managers/wt_remote_manager.py +49 -197
  13. machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +6 -19
  14. machineconfig/cluster/sessions_managers/wt_utils/manager_persistence.py +52 -0
  15. machineconfig/cluster/sessions_managers/wt_utils/monitoring_helpers.py +50 -0
  16. machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +4 -2
  17. machineconfig/cluster/sessions_managers/wt_utils/status_reporting.py +76 -0
  18. machineconfig/cluster/sessions_managers/wt_utils/wt_helpers.py +199 -0
  19. machineconfig/cluster/sessions_managers/zellij_local.py +81 -375
  20. machineconfig/cluster/sessions_managers/zellij_local_manager.py +22 -172
  21. machineconfig/cluster/sessions_managers/zellij_remote.py +40 -41
  22. machineconfig/cluster/sessions_managers/zellij_remote_manager.py +13 -10
  23. machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +4 -8
  24. machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +5 -20
  25. machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +3 -9
  26. machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +3 -1
  27. machineconfig/cluster/sessions_managers/zellij_utils/zellij_local_helper.py +298 -0
  28. machineconfig/cluster/sessions_managers/zellij_utils/zellij_local_helper_restart.py +77 -0
  29. machineconfig/cluster/sessions_managers/zellij_utils/zellij_local_helper_with_panes.py +228 -0
  30. machineconfig/cluster/sessions_managers/zellij_utils/zellij_local_manager_helper.py +165 -0
  31. machineconfig/jobs/{python → installer}/check_installations.py +2 -16
  32. machineconfig/jobs/installer/custom/boxes.py +61 -0
  33. machineconfig/jobs/installer/custom/gh.py +69 -53
  34. machineconfig/jobs/installer/custom/hx.py +77 -20
  35. machineconfig/jobs/installer/custom_dev/alacritty.py +45 -30
  36. machineconfig/jobs/installer/custom_dev/brave.py +43 -35
  37. machineconfig/jobs/installer/custom_dev/bypass_paywall.py +31 -20
  38. machineconfig/jobs/installer/custom_dev/cloudflare_warp_cli.py +23 -0
  39. machineconfig/jobs/installer/custom_dev/code.py +33 -21
  40. machineconfig/jobs/installer/custom_dev/dubdb_adbc.py +30 -0
  41. machineconfig/jobs/installer/custom_dev/espanso.py +64 -41
  42. machineconfig/jobs/installer/custom_dev/goes.py +41 -36
  43. machineconfig/jobs/installer/custom_dev/lvim.py +49 -33
  44. machineconfig/jobs/installer/custom_dev/nerdfont.py +71 -47
  45. machineconfig/jobs/installer/custom_dev/nerfont_windows_helper.py +32 -26
  46. machineconfig/jobs/installer/custom_dev/redis.py +51 -33
  47. machineconfig/jobs/installer/custom_dev/sysabc.py +119 -0
  48. machineconfig/jobs/installer/custom_dev/wezterm.py +55 -39
  49. machineconfig/jobs/installer/custom_dev/winget.py +1 -0
  50. machineconfig/jobs/installer/installer_data.json +3406 -0
  51. machineconfig/jobs/installer/linux_scripts/brave.sh +4 -14
  52. machineconfig/jobs/installer/linux_scripts/{warp-cli.sh → cloudflare_warp_cli.sh} +5 -17
  53. machineconfig/jobs/installer/linux_scripts/docker.sh +5 -17
  54. machineconfig/jobs/installer/linux_scripts/docker_start.sh +6 -14
  55. machineconfig/jobs/installer/linux_scripts/edge.sh +3 -11
  56. machineconfig/jobs/{linux/msc → installer/linux_scripts}/lid.sh +2 -8
  57. machineconfig/jobs/installer/linux_scripts/nerdfont.sh +5 -17
  58. machineconfig/jobs/{linux/msc → installer/linux_scripts}/network.sh +2 -8
  59. machineconfig/jobs/installer/linux_scripts/ngrok.sh +6 -0
  60. machineconfig/jobs/installer/linux_scripts/q.sh +9 -0
  61. machineconfig/jobs/installer/linux_scripts/redis.sh +6 -17
  62. machineconfig/jobs/installer/linux_scripts/vscode.sh +5 -17
  63. machineconfig/jobs/installer/linux_scripts/wezterm.sh +4 -12
  64. machineconfig/jobs/installer/package_groups.py +255 -0
  65. machineconfig/logger.py +0 -1
  66. machineconfig/profile/backup.toml +49 -0
  67. machineconfig/profile/bash_shell_profiles.md +11 -0
  68. machineconfig/profile/create_helper.py +74 -0
  69. machineconfig/profile/create_links.py +288 -0
  70. machineconfig/profile/create_links_export.py +100 -0
  71. machineconfig/profile/create_shell_profile.py +136 -0
  72. machineconfig/profile/mapper.toml +258 -0
  73. machineconfig/scripts/Restore-ThunderbirdProfile.ps1 +92 -0
  74. machineconfig/scripts/__init__.py +0 -4
  75. machineconfig/scripts/linux/{share_cloud.sh → other/share_cloud.sh} +14 -25
  76. machineconfig/scripts/linux/wrap_mcfg +47 -0
  77. machineconfig/scripts/nu/wrap_mcfg.nu +69 -0
  78. machineconfig/scripts/python/agents.py +198 -0
  79. machineconfig/scripts/python/ai/command_runner/command_runner.sh +9 -0
  80. machineconfig/scripts/python/ai/command_runner/prompt.txt +9 -0
  81. machineconfig/scripts/python/ai/generate_files.py +307 -42
  82. machineconfig/scripts/python/ai/initai.py +3 -28
  83. machineconfig/scripts/python/ai/scripts/lint_and_type_check.ps1 +17 -18
  84. machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +17 -18
  85. machineconfig/scripts/python/ai/solutions/_shared.py +9 -1
  86. machineconfig/scripts/python/ai/solutions/copilot/instructions/python/dev.instructions.md +1 -1
  87. machineconfig/scripts/python/ai/solutions/copilot/prompts/pyright_fix.md +16 -0
  88. machineconfig/scripts/python/ai/solutions/generic.py +27 -4
  89. machineconfig/scripts/python/ai/vscode_tasks.py +37 -0
  90. machineconfig/scripts/python/cloud.py +29 -0
  91. machineconfig/scripts/python/croshell.py +117 -181
  92. machineconfig/scripts/python/define.py +31 -0
  93. machineconfig/scripts/python/devops.py +44 -124
  94. machineconfig/scripts/python/devops_navigator.py +10 -0
  95. machineconfig/scripts/python/env_manager/__init__.py +1 -0
  96. machineconfig/scripts/python/env_manager/path_manager_backend.py +47 -0
  97. machineconfig/scripts/python/env_manager/path_manager_tui.py +228 -0
  98. machineconfig/scripts/python/explore.py +49 -0
  99. machineconfig/scripts/python/fire_jobs.py +106 -244
  100. machineconfig/scripts/python/ftpx.py +125 -68
  101. machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_crush.json +14 -0
  102. machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_crush.py +37 -0
  103. machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_cursor_agents.py +22 -0
  104. machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_gemini.py +42 -0
  105. machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_qwen.py +30 -0
  106. machineconfig/scripts/python/helpers_agents/fire_agents_help_launch.py +110 -0
  107. machineconfig/scripts/python/helpers_agents/fire_agents_helper_types.py +34 -0
  108. machineconfig/scripts/python/helpers_agents/fire_agents_load_balancer.py +22 -0
  109. machineconfig/scripts/python/helpers_agents/templates/prompt.txt +6 -0
  110. machineconfig/scripts/python/helpers_agents/templates/template.ps1 +14 -0
  111. machineconfig/scripts/python/helpers_agents/templates/template.sh +24 -0
  112. machineconfig/scripts/python/{cloud_copy.py → helpers_cloud/cloud_copy.py} +30 -23
  113. machineconfig/scripts/python/{cloud_mount.py → helpers_cloud/cloud_mount.py} +11 -19
  114. machineconfig/scripts/python/{cloud_sync.py → helpers_cloud/cloud_sync.py} +12 -18
  115. machineconfig/scripts/python/{helpers → helpers_cloud}/helpers2.py +3 -3
  116. machineconfig/scripts/python/helpers_croshell/crosh.py +39 -0
  117. machineconfig/scripts/python/{start_slidev.py → helpers_croshell/start_slidev.py} +17 -7
  118. machineconfig/scripts/python/helpers_devops/cli_config.py +95 -0
  119. machineconfig/scripts/python/helpers_devops/cli_config_dotfile.py +89 -0
  120. machineconfig/scripts/python/helpers_devops/cli_data.py +25 -0
  121. machineconfig/scripts/python/helpers_devops/cli_nw.py +134 -0
  122. machineconfig/scripts/python/helpers_devops/cli_repos.py +182 -0
  123. machineconfig/scripts/python/helpers_devops/cli_self.py +134 -0
  124. machineconfig/scripts/python/helpers_devops/cli_share_file.py +137 -0
  125. machineconfig/scripts/python/helpers_devops/cli_share_server.py +141 -0
  126. machineconfig/scripts/python/helpers_devops/cli_terminal.py +156 -0
  127. machineconfig/scripts/python/helpers_devops/cli_utils.py +96 -0
  128. machineconfig/scripts/python/{devops_backup_retrieve.py → helpers_devops/devops_backup_retrieve.py} +7 -10
  129. machineconfig/scripts/python/helpers_devops/devops_status.py +511 -0
  130. machineconfig/scripts/python/helpers_devops/devops_update_repos.py +269 -0
  131. machineconfig/scripts/python/helpers_devops/themes/choose_pwsh_theme.ps1 +81 -0
  132. machineconfig/scripts/python/helpers_devops/themes/choose_starship_theme.bash +3 -0
  133. machineconfig/scripts/python/{choose_wezterm_theme.py → helpers_devops/themes/choose_wezterm_theme.py} +2 -2
  134. machineconfig/scripts/python/helpers_fire_command/__init__.py +0 -0
  135. machineconfig/scripts/python/{helpers/helpers4.py → helpers_fire_command/file_wrangler.py} +57 -87
  136. machineconfig/scripts/python/helpers_fire_command/fire_jobs_args_helper.py +145 -0
  137. machineconfig/scripts/python/helpers_fire_command/fire_jobs_route_helper.py +110 -0
  138. machineconfig/scripts/python/helpers_fire_command/fire_jobs_streamlit_helper.py +0 -0
  139. machineconfig/scripts/python/helpers_msearch/__init__.py +5 -0
  140. machineconfig/scripts/{linux → python/helpers_msearch/scripts_linux}/fzfag +1 -1
  141. machineconfig/scripts/{linux → python/helpers_msearch/scripts_linux}/fzfg +1 -1
  142. machineconfig/scripts/{linux → python/helpers_msearch/scripts_linux}/fzfrga +1 -1
  143. machineconfig/scripts/python/helpers_navigator/__init__.py +20 -0
  144. machineconfig/scripts/python/helpers_navigator/command_builder.py +111 -0
  145. machineconfig/scripts/python/helpers_navigator/command_detail.py +44 -0
  146. machineconfig/scripts/python/helpers_navigator/command_tree.py +588 -0
  147. machineconfig/scripts/python/helpers_navigator/data_models.py +28 -0
  148. machineconfig/scripts/python/helpers_navigator/main_app.py +272 -0
  149. machineconfig/scripts/python/helpers_navigator/search_bar.py +15 -0
  150. machineconfig/scripts/python/helpers_repos/action.py +209 -0
  151. machineconfig/scripts/python/helpers_repos/action_helper.py +150 -0
  152. machineconfig/scripts/python/{repos_helper_clone.py → helpers_repos/clone.py} +6 -7
  153. machineconfig/scripts/python/helpers_repos/cloud_repo_sync.py +218 -0
  154. machineconfig/scripts/python/helpers_repos/count_lines.py +348 -0
  155. machineconfig/scripts/python/helpers_repos/count_lines_frontend.py +17 -0
  156. machineconfig/scripts/python/helpers_repos/entrypoint.py +77 -0
  157. machineconfig/scripts/python/helpers_repos/grource.py +340 -0
  158. machineconfig/scripts/python/{repos_helper_record.py → helpers_repos/record.py} +7 -4
  159. machineconfig/scripts/python/helpers_repos/sync.py +66 -0
  160. machineconfig/scripts/python/{repos_helper_update.py → helpers_repos/update.py} +3 -3
  161. machineconfig/scripts/python/helpers_sessions/__init__.py +0 -0
  162. machineconfig/scripts/python/helpers_sessions/sessions_multiprocess.py +58 -0
  163. machineconfig/scripts/python/helpers_utils/download.py +152 -0
  164. machineconfig/scripts/python/helpers_utils/path.py +108 -0
  165. machineconfig/scripts/python/interactive.py +79 -160
  166. machineconfig/scripts/python/machineconfig.py +63 -0
  167. machineconfig/scripts/python/msearch.py +21 -0
  168. machineconfig/scripts/python/nw/__init__.py +0 -0
  169. machineconfig/scripts/python/{devops_add_identity.py → nw/devops_add_identity.py} +1 -3
  170. machineconfig/scripts/python/{devops_add_ssh_key.py → nw/devops_add_ssh_key.py} +74 -44
  171. machineconfig/scripts/{linux → python/nw}/mount_nfs +1 -1
  172. machineconfig/scripts/python/{mount_nfs.py → nw/mount_nfs.py} +19 -16
  173. machineconfig/scripts/{linux → python/nw}/mount_nw_drive +1 -2
  174. machineconfig/scripts/python/{mount_ssh.py → nw/mount_ssh.py} +7 -8
  175. machineconfig/scripts/python/{onetimeshare.py → nw/onetimeshare.py} +0 -1
  176. machineconfig/scripts/python/nw/ssh_debug_linux.py +391 -0
  177. machineconfig/scripts/python/nw/ssh_debug_windows.py +338 -0
  178. machineconfig/scripts/python/{wifi_conn.py → nw/wifi_conn.py} +1 -53
  179. machineconfig/scripts/python/{wsl_windows_transfer.py → nw/wsl_windows_transfer.py} +6 -5
  180. machineconfig/scripts/python/sessions.py +167 -0
  181. machineconfig/scripts/python/terminal.py +127 -0
  182. machineconfig/scripts/python/utils.py +66 -0
  183. machineconfig/scripts/windows/{mount_nfs.ps1 → mounts/mount_nfs.ps1} +1 -3
  184. machineconfig/scripts/windows/{mount_ssh.ps1 → mounts/mount_ssh.ps1} +1 -1
  185. machineconfig/scripts/windows/{share_smb.ps1 → mounts/share_smb.ps1} +0 -6
  186. machineconfig/scripts/windows/wrap_mcfg.ps1 +60 -0
  187. machineconfig/settings/broot/br.sh +0 -4
  188. machineconfig/settings/broot/conf.toml +1 -1
  189. machineconfig/settings/helix/config.toml +16 -0
  190. machineconfig/settings/helix/languages.toml +13 -4
  191. machineconfig/settings/helix/yazi-picker.sh +12 -0
  192. machineconfig/settings/lf/linux/exe/lfcd.sh +1 -0
  193. machineconfig/settings/lf/linux/exe/previewer.sh +9 -3
  194. machineconfig/settings/lf/linux/lfrc +10 -12
  195. machineconfig/settings/lf/windows/fzf_edit.ps1 +2 -2
  196. machineconfig/settings/lf/windows/lfrc +18 -38
  197. machineconfig/settings/lf/windows/mkfile.ps1 +1 -1
  198. machineconfig/settings/linters/.ruff.toml +1 -1
  199. machineconfig/settings/lvim/windows/archive/config_additional.lua +0 -6
  200. machineconfig/settings/marimo/marimo.toml +80 -0
  201. machineconfig/settings/marimo/snippets/globalize.py +34 -0
  202. machineconfig/settings/pistol/pistol.conf +1 -1
  203. machineconfig/settings/shells/bash/init.sh +55 -31
  204. machineconfig/settings/shells/nushell/config.nu +1 -34
  205. machineconfig/settings/shells/nushell/init.nu +127 -0
  206. machineconfig/settings/shells/pwsh/init.ps1 +60 -43
  207. machineconfig/settings/shells/starship/starship.toml +16 -0
  208. machineconfig/settings/shells/wezterm/wezterm.lua +2 -0
  209. machineconfig/settings/shells/wt/settings.json +32 -17
  210. machineconfig/settings/shells/zsh/init.sh +89 -0
  211. machineconfig/settings/svim/linux/init.toml +0 -4
  212. machineconfig/settings/svim/windows/init.toml +0 -3
  213. machineconfig/settings/yazi/init.lua +57 -0
  214. machineconfig/settings/yazi/keymap_linux.toml +79 -0
  215. machineconfig/settings/yazi/keymap_windows.toml +78 -0
  216. machineconfig/settings/yazi/shell/yazi_cd.ps1 +33 -0
  217. machineconfig/settings/yazi/shell/yazi_cd.sh +8 -0
  218. machineconfig/settings/yazi/yazi.toml +13 -0
  219. machineconfig/setup_linux/__init__.py +10 -0
  220. machineconfig/setup_linux/apps_desktop.sh +89 -0
  221. machineconfig/setup_linux/apps_gui.sh +64 -0
  222. machineconfig/setup_linux/{nix → others}/cli_installation.sh +9 -29
  223. machineconfig/setup_linux/ssh/openssh_all.sh +25 -0
  224. machineconfig/setup_linux/ssh/openssh_wsl.sh +38 -0
  225. machineconfig/setup_linux/uv.sh +15 -0
  226. machineconfig/setup_linux/web_shortcuts/interactive.sh +26 -6
  227. machineconfig/setup_mac/__init__.py +16 -0
  228. machineconfig/setup_mac/apps_gui.sh +248 -0
  229. machineconfig/setup_mac/ssh/openssh_setup.sh +114 -0
  230. machineconfig/setup_mac/uv.sh +36 -0
  231. machineconfig/setup_windows/__init__.py +8 -0
  232. machineconfig/setup_windows/others/power_options.ps1 +7 -0
  233. machineconfig/setup_windows/ssh/add-sshkey.ps1 +29 -0
  234. machineconfig/setup_windows/ssh/add_identity.ps1 +11 -0
  235. machineconfig/setup_windows/ssh/openssh-server.ps1 +37 -0
  236. machineconfig/setup_windows/uv.ps1 +10 -0
  237. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +27 -9
  238. machineconfig/setup_windows/web_shortcuts/quick_init.ps1 +16 -0
  239. machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +37 -23
  240. machineconfig/utils/accessories.py +7 -5
  241. machineconfig/utils/cloud/onedrive/README.md +139 -0
  242. machineconfig/utils/code.py +140 -93
  243. machineconfig/utils/files/art/fat_croco.txt +10 -0
  244. machineconfig/utils/files/art/halfwit_croco.txt +9 -0
  245. machineconfig/utils/files/art/happy_croco.txt +22 -0
  246. machineconfig/utils/files/art/water_croco.txt +11 -0
  247. machineconfig/utils/files/ascii_art.py +118 -0
  248. machineconfig/utils/files/dbms.py +257 -0
  249. machineconfig/utils/files/headers.py +68 -0
  250. machineconfig/utils/files/ouch/__init__.py +0 -0
  251. machineconfig/utils/files/ouch/decompress.py +45 -0
  252. machineconfig/utils/files/read.py +95 -0
  253. machineconfig/utils/installer_utils/github_release_bulk.py +2 -12
  254. machineconfig/utils/installer_utils/installer_class.py +68 -126
  255. machineconfig/utils/installer_utils/installer_cli.py +181 -0
  256. machineconfig/utils/installer_utils/{installer_abc.py → installer_locator_utils.py} +38 -85
  257. machineconfig/utils/{installer.py → installer_utils/installer_runner.py} +69 -69
  258. machineconfig/utils/io.py +77 -23
  259. machineconfig/utils/links.py +309 -100
  260. machineconfig/utils/meta.py +255 -0
  261. machineconfig/utils/notifications.py +1 -1
  262. machineconfig/utils/options.py +10 -25
  263. machineconfig/utils/path_extended.py +94 -104
  264. machineconfig/utils/path_helper.py +75 -22
  265. machineconfig/utils/procs.py +50 -74
  266. machineconfig/utils/scheduler.py +94 -97
  267. machineconfig/utils/scheduling.py +0 -3
  268. machineconfig/utils/schemas/fire_agents/fire_agents_input.py +5 -17
  269. machineconfig/utils/schemas/installer/installer_types.py +0 -1
  270. machineconfig/utils/schemas/layouts/layout_types.py +2 -1
  271. machineconfig/utils/source_of_truth.py +3 -6
  272. machineconfig/utils/ssh.py +742 -254
  273. machineconfig/utils/ssh_utils/utils.py +0 -0
  274. machineconfig/utils/terminal.py +3 -140
  275. machineconfig/utils/tst.py +20 -0
  276. machineconfig/utils/upgrade_packages.py +109 -28
  277. machineconfig/utils/ve.py +13 -5
  278. machineconfig-7.66.dist-info/METADATA +124 -0
  279. machineconfig-7.66.dist-info/RECORD +451 -0
  280. machineconfig-7.66.dist-info/entry_points.txt +15 -0
  281. machineconfig/cluster/templates/utils.py +0 -51
  282. machineconfig/jobs/installer/linux_scripts/pgsql.sh +0 -49
  283. machineconfig/jobs/installer/linux_scripts/timescaledb.sh +0 -85
  284. machineconfig/jobs/installer/packages_custom_dev.json +0 -226
  285. machineconfig/jobs/installer/packages_custom_essential.json +0 -39
  286. machineconfig/jobs/installer/packages_github_dev.json +0 -1110
  287. machineconfig/jobs/installer/packages_github_essential.json +0 -804
  288. machineconfig/jobs/linux/msc/cli_agents.sh +0 -37
  289. machineconfig/jobs/python/create_bootable_media.py +0 -16
  290. machineconfig/jobs/python/python_cargo_build_share.py +0 -59
  291. machineconfig/jobs/python/python_ve_symlink.py +0 -29
  292. machineconfig/jobs/python/tasks.py +0 -3
  293. machineconfig/jobs/python/vscode/api.py +0 -49
  294. machineconfig/jobs/python/vscode/sync_code.py +0 -58
  295. machineconfig/jobs/windows/archive/archive_pygraphviz.ps1 +0 -14
  296. machineconfig/jobs/windows/start_terminal.ps1 +0 -6
  297. machineconfig/jobs/windows/startup_file.cmd +0 -2
  298. machineconfig/profile/create.py +0 -170
  299. machineconfig/profile/shell.py +0 -176
  300. machineconfig/scripts/cloud/init.sh +0 -119
  301. machineconfig/scripts/linux/choose_wezterm_theme +0 -3
  302. machineconfig/scripts/linux/cloud_copy +0 -2
  303. machineconfig/scripts/linux/cloud_mount +0 -2
  304. machineconfig/scripts/linux/cloud_repo_sync +0 -2
  305. machineconfig/scripts/linux/cloud_sync +0 -2
  306. machineconfig/scripts/linux/croshell +0 -3
  307. machineconfig/scripts/linux/devops +0 -2
  308. machineconfig/scripts/linux/fire +0 -2
  309. machineconfig/scripts/linux/fire_agents +0 -2
  310. machineconfig/scripts/linux/ftpx +0 -2
  311. machineconfig/scripts/linux/fzf2g +0 -21
  312. machineconfig/scripts/linux/fzffg +0 -25
  313. machineconfig/scripts/linux/gh_models +0 -2
  314. machineconfig/scripts/linux/initai +0 -2
  315. machineconfig/scripts/linux/kill_process +0 -2
  316. machineconfig/scripts/linux/programs +0 -21
  317. machineconfig/scripts/linux/repos +0 -2
  318. machineconfig/scripts/linux/scheduler +0 -2
  319. machineconfig/scripts/linux/share_smb +0 -1
  320. machineconfig/scripts/linux/start_slidev +0 -2
  321. machineconfig/scripts/linux/start_terminals +0 -3
  322. machineconfig/scripts/linux/warp-cli.sh +0 -122
  323. machineconfig/scripts/linux/wifi_conn +0 -2
  324. machineconfig/scripts/linux/z_ls +0 -104
  325. machineconfig/scripts/python/ai/solutions/copilot/prompts/allLintersAndTypeCheckers.prompt.md +0 -5
  326. machineconfig/scripts/python/cloud_repo_sync.py +0 -186
  327. machineconfig/scripts/python/devops_devapps_install.py +0 -159
  328. machineconfig/scripts/python/devops_update_repos.py +0 -180
  329. machineconfig/scripts/python/dotfile.py +0 -52
  330. machineconfig/scripts/python/fire_agents.py +0 -175
  331. machineconfig/scripts/python/fire_agents_help_launch.py +0 -143
  332. machineconfig/scripts/python/fire_agents_load_balancer.py +0 -50
  333. machineconfig/scripts/python/fire_jobs_args_helper.py +0 -75
  334. machineconfig/scripts/python/fire_jobs_layout_helper.py +0 -74
  335. machineconfig/scripts/python/get_zellij_cmd.py +0 -15
  336. machineconfig/scripts/python/gh_models.py +0 -104
  337. machineconfig/scripts/python/helpers/repo_sync_helpers.py +0 -114
  338. machineconfig/scripts/python/repos.py +0 -80
  339. machineconfig/scripts/python/repos_helper_action.py +0 -335
  340. machineconfig/scripts/python/share_terminal.py +0 -104
  341. machineconfig/scripts/python/snapshot.py +0 -25
  342. machineconfig/scripts/python/start_terminals.py +0 -121
  343. machineconfig/scripts/python/t4.py +0 -17
  344. machineconfig/scripts/windows/choose_wezterm_theme.ps1 +0 -1
  345. machineconfig/scripts/windows/cloud_copy.ps1 +0 -1
  346. machineconfig/scripts/windows/cloud_mount.ps1 +0 -1
  347. machineconfig/scripts/windows/cloud_repo_sync.ps1 +0 -1
  348. machineconfig/scripts/windows/cloud_sync.ps1 +0 -1
  349. machineconfig/scripts/windows/croshell.ps1 +0 -1
  350. machineconfig/scripts/windows/devops.ps1 +0 -1
  351. machineconfig/scripts/windows/dotfile.ps1 +0 -1
  352. machineconfig/scripts/windows/fire.ps1 +0 -1
  353. machineconfig/scripts/windows/ftpx.ps1 +0 -1
  354. machineconfig/scripts/windows/gpt.ps1 +0 -1
  355. machineconfig/scripts/windows/grep.ps1 +0 -2
  356. machineconfig/scripts/windows/initai.ps1 +0 -1
  357. machineconfig/scripts/windows/kill_process.ps1 +0 -1
  358. machineconfig/scripts/windows/nano.ps1 +0 -3
  359. machineconfig/scripts/windows/pomodoro.ps1 +0 -1
  360. machineconfig/scripts/windows/reload_path.ps1 +0 -3
  361. machineconfig/scripts/windows/repos.ps1 +0 -1
  362. machineconfig/scripts/windows/scheduler.ps1 +0 -1
  363. machineconfig/scripts/windows/snapshot.ps1 +0 -1
  364. machineconfig/scripts/windows/start_slidev.ps1 +0 -1
  365. machineconfig/scripts/windows/start_terminals.ps1 +0 -1
  366. machineconfig/scripts/windows/wifi_conn.ps1 +0 -2
  367. machineconfig/scripts/windows/wsl_rdp_windows_port_forwarding.ps1 +0 -46
  368. machineconfig/scripts/windows/wsl_ssh_windows_port_forwarding.ps1 +0 -76
  369. machineconfig/settings/lf/linux/exe/fzf_nano.sh +0 -16
  370. machineconfig/setup_linux/others/openssh-server_add_pub_key.sh +0 -57
  371. machineconfig/setup_linux/web_shortcuts/ascii_art.sh +0 -93
  372. machineconfig/setup_linux/web_shortcuts/croshell.sh +0 -11
  373. machineconfig/setup_linux/web_shortcuts/ssh.sh +0 -52
  374. machineconfig/setup_windows/web_shortcuts/all.ps1 +0 -18
  375. machineconfig/setup_windows/web_shortcuts/ascii_art.ps1 +0 -36
  376. machineconfig/setup_windows/web_shortcuts/croshell.ps1 +0 -16
  377. machineconfig/setup_windows/web_shortcuts/ssh.ps1 +0 -11
  378. machineconfig/utils/ai/generate_file_checklist.py +0 -68
  379. machineconfig-3.99.dist-info/METADATA +0 -167
  380. machineconfig-3.99.dist-info/RECORD +0 -409
  381. machineconfig-3.99.dist-info/entry_points.txt +0 -18
  382. machineconfig/cluster/{templates → remote}/run_cloud.py +0 -0
  383. machineconfig/cluster/{templates → remote}/run_cluster.py +0 -0
  384. machineconfig/cluster/{templates → remote}/run_remote.py +0 -0
  385. machineconfig/scripts/linux/{share_nfs → other/share_nfs} +0 -0
  386. machineconfig/scripts/linux/{start_docker → other/start_docker} +0 -0
  387. machineconfig/scripts/linux/{switch_ip → other/switch_ip} +0 -0
  388. machineconfig/{jobs/python → scripts/python/helpers_agents}/__init__.py +0 -0
  389. machineconfig/scripts/python/{helpers → helpers_agents/agentic_frameworks}/__init__.py +0 -0
  390. machineconfig/scripts/python/{fire_agents_help_search.py → helpers_agents/fire_agents_help_search.py} +0 -0
  391. machineconfig/{jobs/windows/msc/cli_agents.bat → scripts/python/helpers_cloud/__init__.py} +0 -0
  392. machineconfig/scripts/python/{helpers → helpers_cloud}/cloud_helpers.py +1 -1
  393. /machineconfig/scripts/python/{helpers → helpers_cloud}/helpers5.py +0 -0
  394. /machineconfig/{jobs/windows/msc/cli_agents.ps1 → scripts/python/helpers_croshell/__init__.py} +0 -0
  395. /machineconfig/scripts/python/{pomodoro.py → helpers_croshell/pomodoro.py} +0 -0
  396. /machineconfig/scripts/python/{scheduler.py → helpers_croshell/scheduler.py} +0 -0
  397. /machineconfig/scripts/python/{viewer.py → helpers_croshell/viewer.py} +0 -0
  398. /machineconfig/scripts/python/{viewer_template.py → helpers_croshell/viewer_template.py} +0 -0
  399. /machineconfig/scripts/python/{fire_jobs_streamlit_helper.py → helpers_devops/__init__.py} +0 -0
  400. /machineconfig/scripts/{windows/share_nfs.ps1 → python/helpers_devops/themes/__init__.py} +0 -0
  401. /machineconfig/{settings/yazi/keymap.toml → scripts/python/helpers_devops/themes/choose_starship_theme.ps1} +0 -0
  402. /machineconfig/scripts/python/{cloud_manager.py → helpers_fire_command/cloud_manager.py} +0 -0
  403. /machineconfig/scripts/{linux → python/helpers_msearch/scripts_linux}/skrg +0 -0
  404. /machineconfig/scripts/{windows → python/helpers_msearch/scripts_windows}/fzfb.ps1 +0 -0
  405. /machineconfig/scripts/{windows → python/helpers_msearch/scripts_windows}/fzfg.ps1 +0 -0
  406. /machineconfig/scripts/{windows → python/helpers_msearch/scripts_windows}/fzfrga.bat +0 -0
  407. /machineconfig/scripts/{linux → python/nw}/mount_drive +0 -0
  408. /machineconfig/scripts/python/{mount_nw_drive.py → nw/mount_nw_drive.py} +0 -0
  409. /machineconfig/scripts/{linux → python/nw}/mount_smb +0 -0
  410. /machineconfig/scripts/windows/{mount_nw.ps1 → mounts/mount_nw.ps1} +0 -0
  411. /machineconfig/scripts/windows/{mount_smb.ps1 → mounts/mount_smb.ps1} +0 -0
  412. /machineconfig/scripts/windows/{share_cloud.cmd → mounts/share_cloud.cmd} +0 -0
  413. /machineconfig/scripts/windows/{unlock_bitlocker.ps1 → mounts/unlock_bitlocker.ps1} +0 -0
  414. /machineconfig/setup_linux/{web_shortcuts → others}/android.sh +0 -0
  415. /machineconfig/{jobs/windows/archive → setup_windows/ssh}/openssh-server_add_key.ps1 +0 -0
  416. /machineconfig/{jobs/windows/archive → setup_windows/ssh}/openssh-server_copy-ssh-id.ps1 +0 -0
  417. {machineconfig-3.99.dist-info → machineconfig-7.66.dist-info}/WHEEL +0 -0
  418. {machineconfig-3.99.dist-info → machineconfig-7.66.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,272 @@
1
+ """
2
+ Main TUI application for navigating machineconfig commands.
3
+ """
4
+
5
+ import subprocess
6
+ from textual.app import App, ComposeResult
7
+ from textual.widgets import Header, Footer, Input, Tree
8
+ from textual.binding import Binding
9
+ from machineconfig.scripts.python.helpers_navigator.command_builder import CommandBuilderScreen
10
+ from machineconfig.scripts.python.helpers_navigator.command_tree import CommandTree
11
+ from machineconfig.scripts.python.helpers_navigator.command_detail import CommandDetail
12
+ from machineconfig.scripts.python.helpers_navigator.search_bar import SearchBar
13
+ from machineconfig.scripts.python.helpers_navigator.data_models import CommandInfo
14
+
15
+
16
+ class CommandNavigatorApp(App[None]):
17
+ """TUI application for navigating machineconfig commands."""
18
+
19
+ CSS = """
20
+ Screen {
21
+ layout: grid;
22
+ grid-size: 2 3;
23
+ grid-rows: auto 1fr auto;
24
+ grid-columns: 1fr 1fr;
25
+ height: 100%;
26
+ width: 100%;
27
+ }
28
+
29
+ Header {
30
+ column-span: 2;
31
+ background: $boost;
32
+ color: $text;
33
+ width: 100%;
34
+ }
35
+
36
+ #search-bar {
37
+ column-span: 2;
38
+ padding: 1;
39
+ background: $surface;
40
+ height: auto;
41
+ width: 100%;
42
+ }
43
+
44
+ .search-label {
45
+ width: auto;
46
+ padding-right: 1;
47
+ }
48
+
49
+ #search-input {
50
+ width: 1fr;
51
+ }
52
+
53
+ #command-tree {
54
+ row-span: 1;
55
+ border: solid $primary;
56
+ padding: 1;
57
+ width: 100%;
58
+ height: 100%;
59
+ }
60
+
61
+ #command-detail {
62
+ row-span: 1;
63
+ border: solid $primary;
64
+ padding: 1;
65
+ width: 100%;
66
+ height: 100%;
67
+ }
68
+
69
+ Footer {
70
+ column-span: 2;
71
+ background: $boost;
72
+ width: 100%;
73
+ }
74
+
75
+ Button {
76
+ margin: 1;
77
+ }
78
+
79
+ CommandBuilderScreen {
80
+ align: center middle;
81
+ }
82
+
83
+ CommandBuilderScreen > VerticalScroll {
84
+ width: 80;
85
+ height: auto;
86
+ max-height: 90%;
87
+ background: $surface;
88
+ border: thick $primary;
89
+ padding: 2;
90
+ }
91
+
92
+ CommandBuilderScreen .title {
93
+ margin-bottom: 1;
94
+ }
95
+
96
+ CommandBuilderScreen Label {
97
+ margin-top: 1;
98
+ margin-bottom: 0;
99
+ }
100
+
101
+ CommandBuilderScreen Input {
102
+ margin-bottom: 1;
103
+ }
104
+
105
+ CommandBuilderScreen .buttons {
106
+ margin-top: 2;
107
+ height: auto;
108
+ align: center middle;
109
+ }
110
+ """
111
+
112
+ BINDINGS = [
113
+ Binding("q", "quit", "Quit", priority=True),
114
+ Binding("c", "copy_command", "Copy Command"),
115
+ Binding("/", "focus_search", "Search"),
116
+ Binding("?", "help", "Help"),
117
+ Binding("r", "run_command", "Run Command"),
118
+ Binding("b", "build_command", "Build Command"),
119
+ ]
120
+
121
+ def compose(self) -> ComposeResult:
122
+ """Create child widgets for the app."""
123
+ yield Header(show_clock=True)
124
+ yield SearchBar(id="search-bar")
125
+ yield CommandTree("📚 machineconfig Commands", id="command-tree")
126
+ yield CommandDetail(id="command-detail")
127
+ yield Footer()
128
+
129
+ def on_mount(self) -> None:
130
+ """Actions when app is mounted."""
131
+ self.title = "machineconfig Command Navigator"
132
+ self.sub_title = "Navigate and explore all available commands"
133
+ tree = self.query_one(CommandTree)
134
+ tree.focus()
135
+
136
+ def on_tree_node_selected(self, event: Tree.NodeSelected[CommandInfo]) -> None:
137
+ """Handle tree node selection."""
138
+ command_info = event.node.data
139
+ detail_widget = self.query_one("#command-detail", CommandDetail)
140
+ detail_widget.update_command(command_info)
141
+
142
+ def on_input_changed(self, event: Input.Changed) -> None:
143
+ """Handle search input changes."""
144
+ if event.input.id != "search-input":
145
+ return
146
+
147
+ search_term = event.value.lower()
148
+ tree = self.query_one(CommandTree)
149
+
150
+ if not search_term:
151
+ # Show all nodes - expand all root children
152
+ for node in tree.root.children:
153
+ node.expand()
154
+ return
155
+
156
+ # Filter nodes based on search term
157
+ def filter_tree(node): # type: ignore
158
+ if node.data and not node.data.is_group:
159
+ match = (search_term in node.data.name.lower() or
160
+ search_term in node.data.description.lower() or
161
+ search_term in node.data.command.lower())
162
+ return match
163
+ return False
164
+
165
+ # Expand parents of matching nodes by walking through all nodes
166
+ def walk_nodes(node): # type: ignore
167
+ """Recursively walk through tree nodes."""
168
+ yield node
169
+ for child in node.children:
170
+ yield from walk_nodes(child)
171
+
172
+ for node in walk_nodes(tree.root):
173
+ if filter_tree(node):
174
+ parent = node.parent
175
+ while parent and parent != tree.root:
176
+ parent.expand()
177
+ parent = parent.parent # type: ignore
178
+
179
+ def action_copy_command(self) -> None:
180
+ """Copy the selected command to clipboard."""
181
+ tree = self.query_one(CommandTree)
182
+ if tree.cursor_node and tree.cursor_node.data:
183
+ command = tree.cursor_node.data.command
184
+ try:
185
+ import pyperclip # type: ignore
186
+ pyperclip.copy(command)
187
+ self.notify(f"Copied: {command}", severity="information")
188
+ except ImportError:
189
+ self.notify("Install pyperclip to enable clipboard support", severity="warning")
190
+
191
+ def action_run_command(self) -> None:
192
+ """Run the selected command without arguments."""
193
+ tree = self.query_one(CommandTree)
194
+ if tree.cursor_node and tree.cursor_node.data:
195
+ command_info = tree.cursor_node.data
196
+ if command_info.is_group:
197
+ self.notify("Cannot run command groups directly", severity="warning")
198
+ return
199
+
200
+ self._execute_command(command_info.command)
201
+
202
+ def action_build_command(self) -> None:
203
+ """Open command builder for selected command."""
204
+ tree = self.query_one(CommandTree)
205
+ if tree.cursor_node and tree.cursor_node.data:
206
+ command_info = tree.cursor_node.data
207
+ if command_info.is_group:
208
+ self.notify("Cannot build command for groups", severity="warning")
209
+ return
210
+
211
+ self.push_screen(CommandBuilderScreen(command_info), self._handle_builder_result)
212
+
213
+ def _handle_builder_result(self, result: str | None) -> None:
214
+ """Handle result from command builder."""
215
+ if not result:
216
+ return
217
+
218
+ if result.startswith("EXECUTE:"):
219
+ command = result[8:]
220
+ self._execute_command(command)
221
+ elif result.startswith("COPY:"):
222
+ command = result[5:]
223
+ self._copy_to_clipboard(command)
224
+
225
+ def _execute_command(self, command: str) -> None:
226
+ """Execute a shell command."""
227
+ try:
228
+ self.notify(f"Executing: {command}", severity="information")
229
+ result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=30)
230
+
231
+ if result.returncode == 0:
232
+ output = result.stdout.strip()
233
+ if output:
234
+ self.notify(f"Success: {output[:100]}...", severity="information", timeout=5)
235
+ else:
236
+ self.notify("Command executed successfully", severity="information")
237
+ else:
238
+ error = result.stderr.strip() or "Unknown error"
239
+ self.notify(f"Error: {error[:100]}...", severity="error", timeout=10)
240
+ except subprocess.TimeoutExpired:
241
+ self.notify("Command timed out after 30 seconds", severity="warning")
242
+ except Exception as e:
243
+ self.notify(f"Failed to execute: {str(e)}", severity="error")
244
+
245
+ def _copy_to_clipboard(self, command: str) -> None:
246
+ """Copy command to clipboard."""
247
+ try:
248
+ import pyperclip # type: ignore
249
+ pyperclip.copy(command)
250
+ self.notify(f"Copied: {command}", severity="information")
251
+ except ImportError:
252
+ self.notify("Install pyperclip to enable clipboard support", severity="warning")
253
+
254
+ def action_focus_search(self) -> None:
255
+ """Focus the search input."""
256
+ search_input = self.query_one("#search-input", Input)
257
+ search_input.focus()
258
+
259
+ def action_help(self) -> None:
260
+ """Show help information."""
261
+ help_text = """
262
+ Navigation:
263
+ - ↑↓: Navigate tree
264
+ - Enter: Expand/collapse node
265
+ - /: Focus search
266
+ - c: Copy command to clipboard
267
+ - r: Run command directly (no args)
268
+ - b: Build command with arguments
269
+ - q: Quit
270
+ - ?: Show this help
271
+ """
272
+ self.notify(help_text, severity="information", timeout=10)
@@ -0,0 +1,15 @@
1
+ """
2
+ Search bar widget for filtering commands.
3
+ """
4
+
5
+ from textual.app import ComposeResult
6
+ from textual.containers import Horizontal
7
+ from textual.widgets import Label, Input
8
+
9
+
10
+ class SearchBar(Horizontal):
11
+ """Search bar widget."""
12
+
13
+ def compose(self) -> ComposeResult:
14
+ yield Label("🔍 Search: ", classes="search-label")
15
+ yield Input(placeholder="Type to search commands...", id="search-input")
@@ -0,0 +1,209 @@
1
+ from machineconfig.scripts.python.helpers_repos.action_helper import GitAction, GitOperationResult, GitOperationSummary, print_git_operations_summary
2
+ from machineconfig.utils.path_extended import PathExtended
3
+ from machineconfig.utils.accessories import randstr
4
+ from machineconfig.scripts.python.helpers_repos.update import update_repository
5
+
6
+ from typing import Optional, Dict, Any, List, cast
7
+ import concurrent.futures
8
+ import os
9
+
10
+ from rich import print as pprint
11
+
12
+
13
+ def git_action(path: PathExtended, action: GitAction, mess: Optional[str], r: bool, auto_uv_sync: bool) -> GitOperationResult:
14
+ """Perform git actions using Python instead of shell scripts. Returns detailed operation result."""
15
+ from git.exc import InvalidGitRepositoryError
16
+ from git.repo import Repo
17
+
18
+ try:
19
+ repo = Repo(str(path), search_parent_directories=False)
20
+ except InvalidGitRepositoryError:
21
+ pprint(f"⚠️ Skipping {path} because it is not a git repository.")
22
+ if r:
23
+ results = [git_action(path=sub_path, action=action, mess=mess, r=r, auto_uv_sync=auto_uv_sync) for sub_path in path.search()]
24
+ # For recursive calls, we need to aggregate results somehow
25
+ # For now, return success if all recursive operations succeeded
26
+ all_successful = all(result.success for result in results)
27
+ return GitOperationResult(
28
+ repo_path=path,
29
+ action=action.value,
30
+ success=all_successful,
31
+ message=f"Recursive operation: {len([r for r in results if r.success])}/{len(results)} succeeded",
32
+ is_git_repo=False,
33
+ )
34
+ else:
35
+ return GitOperationResult(repo_path=path, action=action.value, success=False, message="Not a git repository", is_git_repo=False)
36
+
37
+ print(f">>>>>>>>> 🔧{action} - {path}")
38
+ remote_count = len(repo.remotes)
39
+
40
+ try:
41
+ if action == GitAction.commit:
42
+ if mess is None:
43
+ mess = "auto_commit_" + randstr()
44
+
45
+ # Check if there are changes to commit
46
+ if repo.is_dirty() or repo.untracked_files:
47
+ repo.git.add(A=True) # Stage all changes
48
+ repo.index.commit(mess)
49
+ print(f"✅ Committed changes with message: {mess}")
50
+ return GitOperationResult(
51
+ repo_path=path,
52
+ action=action.value,
53
+ success=True,
54
+ message=f"Committed changes with message: {mess}",
55
+ had_changes=True,
56
+ remote_count=remote_count,
57
+ )
58
+ else:
59
+ print("ℹ️ No changes to commit")
60
+ return GitOperationResult(
61
+ repo_path=path, action=action.value, success=True, message="No changes to commit", had_changes=False, remote_count=remote_count
62
+ )
63
+
64
+ elif action == GitAction.push:
65
+ if not repo.remotes:
66
+ print("⚠️ No remotes configured for push")
67
+ return GitOperationResult(repo_path=path, action=action.value, success=False, message="No remotes configured", remote_count=0)
68
+
69
+ success = True
70
+ failed_remotes = []
71
+ for remote in repo.remotes:
72
+ try:
73
+ print(f"🚀 Pushing to {remote.url}")
74
+ remote.push(repo.active_branch.name)
75
+ print(f"✅ Pushed to {remote.name}")
76
+ except Exception as e:
77
+ print(f"❌ Failed to push to {remote.name}: {e}")
78
+ failed_remotes.append(f"{remote.name}: {str(e)}")
79
+ success = False
80
+
81
+ message = "Push successful" if success else f"Push failed for: {', '.join(failed_remotes)}"
82
+ return GitOperationResult(repo_path=path, action=action.value, success=success, message=message, remote_count=remote_count)
83
+
84
+ elif action == GitAction.pull:
85
+ # Use the enhanced update function with uv sync support
86
+ try:
87
+ update_repository(repo, auto_uv_sync=auto_uv_sync, allow_password_prompt=False)
88
+ print("✅ Pull completed")
89
+ return GitOperationResult(
90
+ repo_path=path, action=action.value, success=True, message="Pull completed successfully", remote_count=remote_count
91
+ )
92
+ except Exception as e:
93
+ print(f"❌ Pull failed: {e}")
94
+ return GitOperationResult(
95
+ repo_path=path, action=action.value, success=False, message=f"Pull failed: {str(e)}", remote_count=remote_count
96
+ )
97
+
98
+ except Exception as e:
99
+ print(f"❌ Error performing {action} on {path}: {e}")
100
+ return GitOperationResult(repo_path=path, action=action.value, success=False, message=f"Error: {str(e)}", remote_count=remote_count)
101
+
102
+ # This should never be reached, but just in case
103
+ return GitOperationResult(repo_path=path, action=action.value, success=False, message="Unknown error", remote_count=remote_count)
104
+
105
+
106
+ def perform_git_operations(repos_root: PathExtended, pull: bool, commit: bool, push: bool, recursive: bool, auto_uv_sync: bool) -> None:
107
+ """Perform git operations on all repositories and provide detailed summary."""
108
+ print(f"\n🔄 Performing Git actions on repositories @ `{repos_root}`...")
109
+ summary = GitOperationSummary()
110
+ # Keep track of which operations we are performing
111
+ operations_performed: List[str] = []
112
+ if pull:
113
+ operations_performed.append("pull")
114
+ if commit:
115
+ operations_performed.append("commit")
116
+ if push:
117
+ operations_performed.append("push")
118
+
119
+ # Collect all candidate paths first
120
+ paths = list(repos_root.search("*"))
121
+
122
+ def _process_path(a_path: PathExtended) -> Dict[str, Any]:
123
+ """Worker that processes a single path and returns metadata and results."""
124
+ from git.exc import InvalidGitRepositoryError
125
+ from git.repo import Repo
126
+
127
+ result_payload: Dict[str, Any] = {"path": a_path, "is_git": False, "results": [], "repo_remotes_count": 0}
128
+ print(f"{('Handling ' + str(a_path)).center(80, '-')}")
129
+
130
+ try:
131
+ repo = Repo(str(a_path), search_parent_directories=False)
132
+ except InvalidGitRepositoryError:
133
+ result_payload["non_git"] = True
134
+ pprint(f"⚠️ Skipping {a_path} because it is not a git repository.")
135
+ return result_payload
136
+
137
+ # It's a git repo
138
+ result_payload["is_git"] = True
139
+ result_payload["repo_remotes_count"] = len(repo.remotes)
140
+
141
+ # Perform configured operations sequentially for this repo (the repo-level work is done concurrently between repos)
142
+ try:
143
+ if pull:
144
+ r = git_action(path=a_path, action=GitAction.pull, mess=None, r=recursive, auto_uv_sync=auto_uv_sync)
145
+ result_payload["results"].append(r)
146
+ if commit:
147
+ r = git_action(path=a_path, action=GitAction.commit, mess=None, r=recursive, auto_uv_sync=auto_uv_sync)
148
+ result_payload["results"].append(r)
149
+ if push:
150
+ r = git_action(path=a_path, action=GitAction.push, mess=None, r=recursive, auto_uv_sync=auto_uv_sync)
151
+ result_payload["results"].append(r)
152
+ except Exception as e:
153
+ # Capture any unexpected exception for this path
154
+ pprint(f"❌ Error processing {a_path}: {e}")
155
+
156
+ return result_payload
157
+
158
+ # Choose a reasonable number of workers
159
+ max_workers = min(32, (os.cpu_count() or 1) * 5, len(paths) or 1)
160
+
161
+ # Run the workers in parallel and aggregate results
162
+ with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as exc:
163
+ future_to_path = {exc.submit(_process_path, p): p for p in paths}
164
+ for fut in concurrent.futures.as_completed(future_to_path):
165
+ payload = fut.result()
166
+ a_path = cast(PathExtended, payload.get("path"))
167
+ summary.total_paths_processed += 1
168
+
169
+ if not payload.get("is_git"):
170
+ summary.non_git_paths += 1
171
+ continue
172
+
173
+ # git repo found
174
+ summary.git_repos_found += 1
175
+ if payload.get("repo_remotes_count", 0) == 0:
176
+ summary.repos_without_remotes.append(a_path)
177
+
178
+ for r in payload.get("results", []):
179
+ action_name = r.action if hasattr(r, "action") else ""
180
+ # Pull
181
+ if action_name == "pull":
182
+ summary.pulls_attempted += 1
183
+ if r.success:
184
+ summary.pulls_successful += 1
185
+ else:
186
+ summary.pulls_failed += 1
187
+ summary.failed_operations.append(r)
188
+ # Commit
189
+ elif action_name == "commit":
190
+ summary.commits_attempted += 1
191
+ if r.success:
192
+ if getattr(r, "had_changes", False):
193
+ summary.commits_successful += 1
194
+ else:
195
+ summary.commits_no_changes += 1
196
+ else:
197
+ summary.commits_failed += 1
198
+ summary.failed_operations.append(r)
199
+ # Push
200
+ elif action_name == "push":
201
+ summary.pushes_attempted += 1
202
+ if r.success:
203
+ summary.pushes_successful += 1
204
+ else:
205
+ summary.pushes_failed += 1
206
+ summary.failed_operations.append(r)
207
+
208
+ # Print the detailed summary
209
+ print_git_operations_summary(summary, operations_performed)
@@ -0,0 +1,150 @@
1
+ from enum import Enum
2
+ from machineconfig.utils.path_extended import PathExtended
3
+
4
+
5
+ from dataclasses import dataclass
6
+
7
+ from rich.columns import Columns
8
+ from rich.panel import Panel
9
+ from rich.table import Table
10
+
11
+
12
+ @dataclass
13
+ class GitOperationResult:
14
+ """Result of a git operation on a single repository."""
15
+ repo_path: PathExtended
16
+ action: str
17
+ success: bool
18
+ message: str
19
+ is_git_repo: bool = True
20
+ had_changes: bool = False
21
+ remote_count: int = 0
22
+
23
+
24
+ class GitAction(Enum):
25
+ commit = "commit"
26
+ push = "push"
27
+ pull = "pull"
28
+
29
+
30
+ @dataclass
31
+ class GitOperationSummary:
32
+ """Summary of all git operations performed."""
33
+
34
+ # Basic statistics
35
+ total_paths_processed: int = 0
36
+ git_repos_found: int = 0
37
+ non_git_paths: int = 0
38
+
39
+ # Per-operation statistics
40
+ commits_attempted: int = 0
41
+ commits_successful: int = 0
42
+ commits_no_changes: int = 0
43
+ commits_failed: int = 0
44
+
45
+ pulls_attempted: int = 0
46
+ pulls_successful: int = 0
47
+ pulls_failed: int = 0
48
+
49
+ pushes_attempted: int = 0
50
+ pushes_successful: int = 0
51
+ pushes_failed: int = 0
52
+
53
+ def __post_init__(self):
54
+ self.failed_operations: list[GitOperationResult] = []
55
+ self.repos_without_remotes: list[PathExtended] = []
56
+
57
+
58
+ def print_git_operations_summary(summary: GitOperationSummary, operations_performed: list[str]) -> None:
59
+ """Print a detailed summary of git operations with rich formatting and tables."""
60
+ from rich.console import Console
61
+
62
+ console = Console()
63
+
64
+ # Main summary panel
65
+ summary_stats = [
66
+ f"Total paths processed: {summary.total_paths_processed}",
67
+ f"Git repositories found: {summary.git_repos_found}",
68
+ f"Non-git paths skipped: {summary.non_git_paths}",
69
+ ]
70
+
71
+ console.print(Panel.fit("\n".join(summary_stats), title="[bold blue]📊 Git Operations Summary[/bold blue]", border_style="blue"))
72
+
73
+ # Statistics panels in columns
74
+ stat_panels = []
75
+
76
+ if "commit" in operations_performed:
77
+ commit_stats = [
78
+ f"Attempted: {summary.commits_attempted}",
79
+ f"Successful: {summary.commits_successful}",
80
+ f"No changes: {summary.commits_no_changes}",
81
+ f"Failed: {summary.commits_failed}",
82
+ ]
83
+ stat_panels.append(Panel.fit("\n".join(commit_stats), title="[bold green]💾 Commit Operations[/bold green]", border_style="green"))
84
+
85
+ if "pull" in operations_performed:
86
+ pull_stats = [f"Attempted: {summary.pulls_attempted}", f"Successful: {summary.pulls_successful}", f"Failed: {summary.pulls_failed}"]
87
+ stat_panels.append(Panel.fit("\n".join(pull_stats), title="[bold cyan]⬇️ Pull Operations[/bold cyan]", border_style="cyan"))
88
+
89
+ if "push" in operations_performed:
90
+ push_stats = [f"Attempted: {summary.pushes_attempted}", f"Successful: {summary.pushes_successful}", f"Failed: {summary.pushes_failed}"]
91
+ stat_panels.append(Panel.fit("\n".join(push_stats), title="[bold magenta]🚀 Push Operations[/bold magenta]", border_style="magenta"))
92
+
93
+ if stat_panels:
94
+ console.print(Columns(stat_panels, equal=True, expand=True))
95
+
96
+ # Repositories without remotes warning
97
+ if summary.repos_without_remotes:
98
+ repos_table = Table(title="[bold yellow]⚠️ Repositories Without Remotes[/bold yellow]")
99
+ repos_table.add_column("Repository Name", style="cyan", no_wrap=True)
100
+ repos_table.add_column("Full Path", style="dim")
101
+
102
+ for repo_path in summary.repos_without_remotes:
103
+ repos_table.add_row(repo_path.name, str(repo_path))
104
+
105
+ console.print(repos_table)
106
+ console.print("[yellow]These repositories cannot be pushed to remote servers.[/yellow]")
107
+ elif "push" in operations_performed:
108
+ console.print("[green]✅ All repositories have remote configurations.[/green]")
109
+
110
+ # Failed operations table
111
+ if summary.failed_operations:
112
+ failed_table = Table(title=f"[bold red]❌ Failed Operations ({len(summary.failed_operations)} total)[/bold red]")
113
+ failed_table.add_column("Action", style="bold red", no_wrap=True)
114
+ failed_table.add_column("Repository", style="cyan", no_wrap=True)
115
+ failed_table.add_column("Problem", style="red")
116
+
117
+ # Group failed operations by type for better organization
118
+ failed_by_action = {}
119
+ for failed_op in summary.failed_operations:
120
+ if failed_op.action not in failed_by_action:
121
+ failed_by_action[failed_op.action] = []
122
+ failed_by_action[failed_op.action].append(failed_op)
123
+
124
+ for action, failures in failed_by_action.items():
125
+ for failure in failures:
126
+ repo_name = failure.repo_path.name if failure.is_git_repo else f"{failure.repo_path.name} (not git repo)"
127
+ problem = failure.message if failure.is_git_repo else "Not a git repository"
128
+ failed_table.add_row(action.upper(), repo_name, problem)
129
+
130
+ console.print(failed_table)
131
+ else:
132
+ console.print("[green]✅ All git operations completed successfully![/green]")
133
+
134
+ # Overall success assessment
135
+ total_failed = len(summary.failed_operations)
136
+ total_operations = summary.commits_attempted + summary.pulls_attempted + summary.pushes_attempted
137
+
138
+ if total_failed == 0 and total_operations > 0:
139
+ console.print(f"\n[bold green]🎉 SUCCESS: All {total_operations} operations completed successfully![/bold green]")
140
+ elif total_operations == 0:
141
+ console.print("\n[blue]📝 No git operations were performed.[/blue]")
142
+ else:
143
+ success_rate = ((total_operations - total_failed) / total_operations * 100) if total_operations > 0 else 0
144
+ if total_failed > 0:
145
+ console.print(
146
+ f"\n[bold yellow]⚖️ SUMMARY: {total_operations - total_failed}/{total_operations} operations succeeded ({success_rate:.1f}% success rate)[/bold yellow]"
147
+ )
148
+ console.print("[yellow]Review the failed operations table above for details on what needs attention.[/yellow]")
149
+ else:
150
+ console.print(f"\n[bold green]⚖️ SUMMARY: {total_operations}/{total_operations} operations succeeded (100% success rate)[/bold green]")
@@ -1,5 +1,5 @@
1
- from __future__ import annotations
2
1
 
2
+ from pathlib import Path
3
3
  from typing import Literal, Optional, cast
4
4
 
5
5
  from git import Repo as GitRepo
@@ -7,7 +7,6 @@ from git.exc import GitCommandError
7
7
  from rich import print as pprint
8
8
  from rich.progress import BarColumn, MofNCompleteColumn, Progress, SpinnerColumn, TextColumn, TimeElapsedColumn
9
9
 
10
- from machineconfig.utils.path_extended import PathExtended as PathExtended
11
10
  from machineconfig.utils.schemas.repos.repos_types import RepoRecordDict, RepoRecordFile, RepoRemote
12
11
  from machineconfig.utils.io import read_json
13
12
 
@@ -26,8 +25,8 @@ def choose_remote(remotes: list[RepoRemote], preferred_remote: Optional[str]) ->
26
25
  return remotes[0] if len(remotes) > 0 else None
27
26
 
28
27
 
29
- def ensure_destination(parent_dir: str, name: str) -> PathExtended:
30
- parent_path = PathExtended(parent_dir).expanduser().absolute()
28
+ def ensure_destination(parent_dir: str, name: str) -> Path:
29
+ parent_path = Path(parent_dir).expanduser().absolute()
31
30
  parent_path.mkdir(parents=True, exist_ok=True)
32
31
  return parent_path.joinpath(name)
33
32
 
@@ -94,9 +93,9 @@ def clone_single_repo(repo_spec: RepoRecordDict, preferred_remote: Optional[str]
94
93
  return (status, message)
95
94
 
96
95
 
97
- def clone_repos(spec_path: PathExtended, preferred_remote: Optional[str], checkout_branch_flag: bool, checkout_commit_flag: bool) -> list[tuple[CloneStatus, str]]:
98
- data = cast(RepoRecordFile, read_json(path=spec_path))
99
- repos = data["repos"]
96
+ def clone_repos(spec_path: Path, preferred_remote: Optional[str], checkout_branch_flag: bool, checkout_commit_flag: bool) -> list[tuple[CloneStatus, str]]:
97
+ spec_file = cast(RepoRecordFile, read_json(path=spec_path))
98
+ repos = spec_file["repos"]
100
99
  results: list[tuple[CloneStatus, str]] = []
101
100
  with Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), BarColumn(), MofNCompleteColumn(), TimeElapsedColumn()) as progress:
102
101
  task_id = progress.add_task("Processing repositories...", total=len(repos))