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,218 @@
1
+
2
+ from typing import Optional, Literal, Annotated
3
+
4
+ import typer
5
+
6
+
7
+ def main(
8
+ cloud: Annotated[Optional[str], typer.Option(..., "--cloud", "-c", help="Cloud storage profile name. If not provided, uses default from config.")] = None,
9
+ repo: Annotated[Optional[str], typer.Option(..., "--repo", "-r", help="Path to the local repository. Defaults to current working directory.")] = None,
10
+ message: Annotated[Optional[str], typer.Option(..., "--message", "-m", help="Commit message for local changes.")] = None,
11
+ on_conflict: Annotated[Literal["ask", "a",
12
+ "push-local-merge", "p",
13
+ "overwrite-local", "o",
14
+ "stop-on-conflict", "s",
15
+ "remove-rclone-conflict", "r"
16
+ ], typer.Option(..., "--on-conflict", "-o", help="Action to take on merge conflict. Default is 'ask'.")] = "ask",
17
+ pwd: Annotated[Optional[str], typer.Option(..., "--password", help="Password for encryption/decryption of the remote repository.")] = None,
18
+ ):
19
+ on_conflict_mapper: dict[str, Literal["ask", "push-local-merge", "overwrite-local", "stop-on-conflict", "remove-rclone-conflict"]] = {
20
+ "a": "ask",
21
+ "ask": "ask",
22
+ "p": "push-local-merge",
23
+ "push-local-merge": "push-local-merge",
24
+ "o": "overwrite-local",
25
+ "overwrite-local": "overwrite-local",
26
+ "s": "stop-on-conflict",
27
+ "stop-on-conflict": "stop-on-conflict",
28
+ "r": "remove-rclone-conflict",
29
+ "remove-rclone-conflict": "remove-rclone-conflict",
30
+ }
31
+ on_conflict = on_conflict_mapper[on_conflict]
32
+ import git
33
+ from rich.console import Console
34
+ from rich.panel import Panel
35
+
36
+ from machineconfig.utils.path_extended import PathExtended
37
+ from machineconfig.utils.terminal import Response
38
+ from machineconfig.utils.source_of_truth import CONFIG_ROOT, DEFAULTS_PATH
39
+ from machineconfig.utils.code import get_uv_command_executing_python_script
40
+ from pathlib import Path
41
+ import platform
42
+ import subprocess
43
+ console = Console()
44
+
45
+ if cloud is None:
46
+ try:
47
+ from machineconfig.utils.io import read_ini
48
+
49
+ cloud_resolved = read_ini(DEFAULTS_PATH)["general"]["rclone_config_name"]
50
+ console.print(Panel(f"⚠️ Using default cloud: `{cloud_resolved}` from {DEFAULTS_PATH}", title="Default Cloud", border_style="yellow"))
51
+ except FileNotFoundError:
52
+ console.print(Panel(f"❌ ERROR: No cloud profile found\nLocation: {DEFAULTS_PATH}\nPlease set one up or provide one via the --cloud flag.", title="Error", border_style="red"))
53
+ return ""
54
+ else:
55
+ cloud_resolved = cloud
56
+ repo_local_root = PathExtended.cwd() if repo is None else PathExtended(repo).expanduser().absolute()
57
+ try:
58
+ repo_local_obj = git.Repo(repo_local_root, search_parent_directories=True)
59
+ except git.InvalidGitRepositoryError:
60
+ typer.echo(f"[red]Error:[/] The specified path '{repo_local_root}' is not a valid git repository.")
61
+ typer.Exit(code=1)
62
+ return ""
63
+ repo_local_root = PathExtended(repo_local_obj.working_dir) # cwd might have been in a sub directory of repo_root, so its better to redefine it.
64
+ local_relative_home = PathExtended(repo_local_root.expanduser().absolute().relative_to(Path.home()))
65
+ PathExtended(CONFIG_ROOT).joinpath("remote").mkdir(parents=True, exist_ok=True)
66
+ repo_remote_root = PathExtended(CONFIG_ROOT).joinpath("remote", local_relative_home)
67
+ repo_remote_root.delete(sure=True)
68
+ try:
69
+ console.print(Panel("📥 DOWNLOADING REMOTE REPOSITORY", title_align="left", border_style="blue"))
70
+ remote_path = repo_local_root.get_remote_path(rel2home=True, os_specific=False, root="myhome") + ".zip.enc"
71
+ res = repo_remote_root.from_cloud(remotepath=remote_path, cloud=cloud_resolved, unzip=True, decrypt=True, rel2home=True, os_specific=False, pwd=pwd)
72
+ if res is None:
73
+ raise AssertionError("Remote repo does not exist.")
74
+ except AssertionError:
75
+ console.print(Panel("🆕 Remote repository doesn't exist\n📤 Creating new remote and exiting...", title_align="left", border_style="green"))
76
+ repo_local_root.to_cloud(cloud=cloud_resolved, zip=True, encrypt=True, rel2home=True, pwd=pwd, os_specific=False)
77
+ return ""
78
+
79
+ repo_remote_obj = git.Repo(repo_remote_root)
80
+ if repo_remote_obj.is_dirty():
81
+ console.print(Panel(f"⚠️ WARNING: REMOTE REPOSITORY IS DIRTY\nLocation: {repo_remote_root}\nPlease commit or stash changes before proceeding.", title="Warning", border_style="yellow"))
82
+
83
+ script = f"""
84
+ echo ""
85
+ echo 'echo -e "\\033[1;34m═════ COMMITTING LOCAL CHANGES ═════\\033[0m"'
86
+ cd {repo_local_root}
87
+ git status
88
+ git add .
89
+ git commit -am "{message}"
90
+ echo ""
91
+ echo ""
92
+ echo 'echo -e "\\033[1;34m═════ PULLING LATEST FROM REMOTE ═════\\033[0m"'
93
+ cd {repo_local_root}
94
+ echo '-> Trying to removing originEnc remote from local repo if it exists.'
95
+ # git remote remove originEnc
96
+ git remote remove originEnc 2>/dev/null || true
97
+ echo '-> Adding originEnc remote to local repo'
98
+ git remote add originEnc {repo_remote_root}
99
+ echo '-> Fetching originEnc remote.'
100
+ git pull originEnc master
101
+
102
+ """
103
+
104
+ if Path.home().joinpath("code/machineconfig").exists():
105
+ uv_project_dir = f"""{str(Path.home().joinpath("code/machineconfig"))}"""
106
+ uv_with = None
107
+ else:
108
+ uv_with = ["machineconfig>=7.66"]
109
+ uv_project_dir = None
110
+
111
+ import tempfile
112
+ shell_path = Path(tempfile.mkstemp(suffix=".ps1" if platform.system() == "Windows" else ".sh")[1])
113
+ shell_path.write_text(script, encoding="utf-8")
114
+
115
+ command = f". {shell_path}"
116
+ if platform.system() == "Windows":
117
+ completed = subprocess.run(["powershell", "-Command", command], capture_output=True, check=False, text=True)
118
+ else:
119
+ completed = subprocess.run(command, shell=True, capture_output=True, check=False, text=True)
120
+ res = Response.from_completed_process(completed).capture().print()
121
+
122
+ if res.is_successful(strict_err=True, strict_returcode=True):
123
+ console.print(Panel("✅ Pull succeeded!\n🧹 Removing originEnc remote and local copy\n📤 Pushing merged repository to cloud storage", title="Success", border_style="green"))
124
+ repo_remote_root.delete(sure=True)
125
+ from git.remote import Remote
126
+
127
+ Remote.remove(repo_local_obj, "originEnc")
128
+ repo_local_root.to_cloud(cloud=cloud_resolved, zip=True, encrypt=True, rel2home=True, pwd=pwd, os_specific=False)
129
+ return "success"
130
+ else:
131
+ console.print(Panel(f"⚠️ MERGE FAILED\n💾 Keeping local copy of remote at:\n📂 {repo_remote_root}", title="Merge Failed", border_style="red"))
132
+
133
+ # ================================================================================
134
+ option1 = "Delete remote copy and push local:"
135
+ from machineconfig.utils.meta import lambda_to_python_script
136
+ def func2(remote_repo: str, local_repo: str, cloud: str):
137
+ from machineconfig.scripts.python.helpers_repos.sync import delete_remote_repo_copy_and_push_local
138
+ delete_remote_repo_copy_and_push_local(remote_repo=remote_repo, local_repo=local_repo, cloud=cloud)
139
+ program_1_py = lambda_to_python_script(lambda: func2(remote_repo=str(repo_remote_root), local_repo=str(repo_local_root), cloud=str(cloud_resolved)),
140
+ in_global=True, import_module=False)
141
+ program1, _pyfile1 = get_uv_command_executing_python_script(python_script=program_1_py, uv_with=uv_with, uv_project_dir=uv_project_dir)
142
+ # ================================================================================
143
+ option2 = "Delete local repo and replace it with remote copy:"
144
+ program_2 = f"""
145
+ rm -rfd {repo_local_root}
146
+ mv {repo_remote_root} {repo_local_root}
147
+ """
148
+ if platform.system() in ["Linux", "Darwin"]:
149
+ program_2 += """
150
+ sudo chmod 600 $HOME/.ssh/*
151
+ sudo chmod 700 $HOME/.ssh
152
+ sudo chmod +x $HOME/dotfiles/scripts/linux -R
153
+ """
154
+ import tempfile
155
+ shell_file_2 = Path(tempfile.mkstemp(suffix=".ps1" if platform.system() == "Windows" else ".sh")[1])
156
+ shell_file_2.write_text(program_2, encoding="utf-8")
157
+
158
+ # ================================================================================
159
+ option3 = "Inspect repos:"
160
+ def func(repo_local_root: str, repo_remote_root: str):
161
+ from machineconfig.scripts.python.helpers_repos.sync import inspect_repos
162
+ inspect_repos(repo_local_root=repo_local_root, repo_remote_root=repo_remote_root)
163
+ # program_3_py = function_to_script(func=func, call_with_kwargs={"repo_local_root": str(repo_local_root), "repo_remote_root": str(repo_remote_root)})
164
+ # shell_file_3 = get_shell_file_executing_python_script(python_script=program_3_py, ve_path=None, executable=executable)
165
+ program_3_py = lambda_to_python_script(lambda: func(repo_local_root=str(repo_local_root), repo_remote_root=str(repo_remote_root)),
166
+ in_global=True, import_module=False)
167
+ program3, _pyfile3 = get_uv_command_executing_python_script(python_script=program_3_py, uv_with=uv_with, uv_project_dir=uv_project_dir)
168
+ # ================================================================================
169
+
170
+ option4 = "Remove problematic rclone file from repo and replace with remote:"
171
+ program_4 = f"""
172
+ rm $HOME/dotfiles/creds/rclone/rclone.conf
173
+ cp $HOME/.config/machineconfig/remote/dotfiles/creds/rclone/rclone.conf $HOME/dotfiles/creds/rclone
174
+ cd $HOME/dotfiles
175
+ git commit -am "finished merging"
176
+ {program1}
177
+ """
178
+ shell_file_4 = Path(tempfile.mkstemp(suffix=".ps1" if platform.system() == "Windows" else ".sh")[1])
179
+ shell_file_4.write_text(program_4, encoding="utf-8")
180
+ # ================================================================================
181
+
182
+ console.print(Panel("🔄 RESOLVE MERGE CONFLICT\nChoose an option to resolve the conflict:", title_align="left", border_style="blue"))
183
+
184
+ print(f"• {option1:75} 👉 {program1}")
185
+ print(f"• {option2:75} 👉 {shell_file_2}")
186
+ print(f"• {option3:75} 👉 {program3}")
187
+ print(f"• {option4:75} 👉 {shell_file_4}")
188
+ print("\n\n")
189
+
190
+ program_content = None
191
+ match on_conflict:
192
+ case "ask":
193
+ import questionary
194
+ choice = questionary.select("Choose one option:", choices=[option1, option2, option3, option4]).ask()
195
+ if choice == option1:
196
+ program_content = program1
197
+ elif choice == option2:
198
+ program_content = program_2
199
+ elif choice == option3:
200
+ program_content = program3
201
+ elif choice == option4:
202
+ program_content = program_4
203
+ else:
204
+ raise NotImplementedError(f"Choice {choice} not implemented.")
205
+ case "push-local-merge":
206
+ program_content = program1
207
+ case "overwrite-local":
208
+ program_content = program_2
209
+ case "stop-on-conflict":
210
+ program_content = program3
211
+ case "remove-rclone-conflict":
212
+ program_content = program_4
213
+ case _:
214
+ raise ValueError(f"Unknown action: {on_conflict}")
215
+ from machineconfig.utils.code import run_shell_script
216
+ run_shell_script(script=program_content)
217
+ return program_content
218
+
@@ -0,0 +1,348 @@
1
+
2
+ from typing import TYPE_CHECKING, Annotated
3
+ from git import Repo
4
+ from collections import defaultdict
5
+ from datetime import datetime
6
+
7
+ from pathlib import Path
8
+ from rich.progress import track
9
+ import typer
10
+
11
+
12
+ if TYPE_CHECKING:
13
+ from typing import Any, Dict, List, Optional, Union
14
+ import polars as pl
15
+
16
+
17
+ app = typer.Typer()
18
+
19
+
20
+ def count_lines_in_commit(commit: "Any") -> int:
21
+ _total_lines = 0
22
+ for _file in commit.stats.files:
23
+ if str(_file).endswith(".py"):
24
+ _blob = commit.tree / _file
25
+ _total_lines += len(_blob.data_stream.read().decode("utf-8").splitlines())
26
+ return _total_lines
27
+
28
+
29
+ def count_historical_loc(repo_path: str) -> int:
30
+ repo = Repo(repo_path)
31
+ file_line_counts: "Dict[str, int]" = defaultdict(int)
32
+ total_commits: int = sum(1 for _ in repo.iter_commits())
33
+ print(f"Total commits to process: {total_commits}")
34
+ for i, commit in enumerate(repo.iter_commits(), 1):
35
+ if i % 100 == 0 or i == total_commits:
36
+ print(f"Processing commit {i}/{total_commits} ({i / total_commits:.1%})")
37
+ try:
38
+ # Handle initial commits that have no parents
39
+ if not commit.parents:
40
+ # For initial commit, count all lines in Python files
41
+ for file in commit.stats.files:
42
+ if str(file).endswith(".py"):
43
+ file_line_counts[str(file)] += commit.stats.files[file]["insertions"]
44
+ else:
45
+ # For commits with parents, use stats
46
+ for file in commit.stats.files:
47
+ if str(file).endswith(".py"):
48
+ file_line_counts[str(file)] += commit.stats.files[file]["insertions"]
49
+ except Exception:
50
+ # If stats fail (e.g., corrupted parent), skip this commit
51
+ print(f"Warning: Could not get stats for commit {commit.hexsha[:8]}, skipping")
52
+ continue
53
+
54
+ print(f"\nProcessed files: {len(file_line_counts)}")
55
+ return sum(file_line_counts.values())
56
+
57
+ def count_python_lines(commit: "Any") -> int:
58
+ """Count total lines in Python files for a specific commit"""
59
+ total_lines = 0
60
+ try:
61
+ for blob in commit.tree.traverse():
62
+ if blob.path.endswith(".py"):
63
+ try:
64
+ content = blob.data_stream.read().decode("utf-8")
65
+ total_lines += len(content.splitlines())
66
+ except Exception as _e:
67
+ continue
68
+ except Exception as _e:
69
+ return 0
70
+ return total_lines
71
+ def get_default_branch(repo: Repo) -> str:
72
+ """Get the default branch name of the repository"""
73
+ try:
74
+ _ = repo.refs["main"]
75
+ return "main" # First try 'main'
76
+ except IndexError:
77
+ try:
78
+ _ = repo.refs["master"]
79
+ return "master" # Then try 'master'
80
+ except IndexError:
81
+ return repo.head.reference.name # If neither exists, get the branch the HEAD is pointing to
82
+
83
+
84
+ @app.command()
85
+ def count_historical(repo_path: Annotated[str, typer.Argument(..., help="Path to the git repository")]):
86
+ """Count total historical lines of Python code in the repository."""
87
+ print(f"Analyzing repository: {repo_path}")
88
+ total_loc: int = count_historical_loc(repo_path)
89
+ print(f"\nTotal historical lines of Python code: {total_loc}")
90
+
91
+
92
+ @app.command()
93
+ def analyze_over_time(repo_path: Annotated[str, typer.Argument(..., help="Path to the git repository")]):
94
+ """Analyze a git repository to track Python code size over time with visualization."""
95
+ repo: Repo = Repo(repo_path)
96
+ branch_name: str = get_default_branch(repo)
97
+ print(f"🔍 Using branch: {branch_name}")
98
+ commit_data: "List[Dict[str, Any]]" = []
99
+ print("⏳ Analyzing commits...")
100
+ try:
101
+ commits = list(repo.iter_commits(branch_name))
102
+ from datetime import timezone
103
+
104
+ for commit in track(commits, description="Processing commits..."):
105
+ commit_data.append({"hash": commit.hexsha, "dtmExit": datetime.fromtimestamp(commit.committed_date, tz=timezone.utc), "lines": count_python_lines(commit)})
106
+ except Exception as e:
107
+ print(f"❌ Error analyzing commits: {str(e)}")
108
+ return
109
+
110
+ import polars as pl
111
+ import plotly.graph_objects as go
112
+
113
+ df = pl.DataFrame(commit_data)
114
+ df = df.sort("dtmExit")
115
+ # Create interactive plotly figure with dark theme and all bells and whistles
116
+ fig = go.Figure()
117
+ # Add line chart with gradient fill and sparkle effect
118
+ fig.add_trace(go.Scatter(x=df["dtmExit"], y=df["lines"], mode="lines", line={"width": 3, "color": "#00b4ff"}, fill="tozeroy", fillcolor="rgba(0, 180, 255, 0.2)", name="Lines of Code", hovertemplate="<b>Date:</b> %{x}<br><b>Lines:</b> %{y:,}<extra></extra>"))
119
+ # Add markers for significant points (min, max, last)
120
+ min_idx = df["lines"].arg_min()
121
+ max_idx = df["lines"].arg_max()
122
+ min_point = df.slice(min_idx, 1).to_dicts()[0] if min_idx is not None else {}
123
+ max_point = df.slice(max_idx, 1).to_dicts()[0] if max_idx is not None else {}
124
+ last_point = df.slice(-1, 1).to_dicts()[0]
125
+
126
+ # Add markers for significant points
127
+ fig.add_trace(
128
+ go.Scatter(
129
+ x=[min_point["dtmExit"], max_point["dtmExit"], last_point["dtmExit"]],
130
+ y=[min_point["lines"], max_point["lines"], last_point["lines"]],
131
+ mode="markers",
132
+ marker={"size": [10, 14, 12], "color": ["#ff4f4f", "#4fff4f", "#4f4fff"], "line": {"width": 2, "color": "white"}, "symbol": ["circle", "star", "diamond"]},
133
+ name="Key Points",
134
+ hovertemplate="<b>%{text}</b><br>Date: %{x}<br>Lines: %{y:,}<extra></extra>",
135
+ text=[f"🔽 Min: {min_point['lines']:,} lines", f"🔼 Max: {max_point['lines']:,} lines", f"📊 Current: {last_point['lines']:,} lines"],
136
+ )
137
+ )
138
+
139
+ # Add annotation only for current point
140
+ # annotations = [
141
+ # {"x": last_point['date'], "y": last_point['lines'], "text": f"📊 Current: {last_point['lines']:,} lines", "showarrow": True, "arrowhead": 2, "arrowsize": 1,
142
+ # "arrowwidth": 2, "arrowcolor": "#ffffff", "font": {"size": 14, "color": "#ffffff"}, "bgcolor": "#00b4ff", "bordercolor": "#ffffff",
143
+ # "borderwidth": 1, "borderpad": 4, "ax": 40, "ay": -40}
144
+ # ]
145
+
146
+ # Update layout with dark theme and customizations
147
+ fig.update_layout(
148
+ title={"text": "✨ Python Code Base Size Over Time ✨", "y": 0.95, "x": 0.5, "xanchor": "center", "yanchor": "top", "font": {"size": 24, "color": "white"}},
149
+ xaxis_title="Date 📅",
150
+ yaxis_title="Lines of Code 📝",
151
+ hovermode="closest",
152
+ template="plotly_dark",
153
+ plot_bgcolor="rgba(25, 25, 35, 1)",
154
+ paper_bgcolor="rgba(15, 15, 25, 1)",
155
+ font={"family": "Arial, sans-serif", "size": 14, "color": "white"}, # annotations=annotations,
156
+ autosize=True,
157
+ height=700,
158
+ margin={"l": 80, "r": 80, "t": 100, "b": 80},
159
+ xaxis={"showgrid": True, "gridcolor": "rgba(80, 80, 100, 0.2)", "showline": True, "linecolor": "rgba(200, 200, 255, 0.2)", "tickfont": {"size": 12}},
160
+ yaxis={"showgrid": True, "gridcolor": "rgba(80, 80, 100, 0.2)", "showline": True, "linecolor": "rgba(200, 200, 255, 0.2)", "tickformat": ",", "tickfont": {"size": 12}},
161
+ )
162
+
163
+ # Add range slider for date selection
164
+ fig.update_xaxes(rangeslider_visible=True, rangeslider_thickness=0.05)
165
+
166
+ # Save as interactive HTML and static image
167
+ plot_dir = Path.home().joinpath("tmp_results", "tmp_images", Path(repo_path).name)
168
+ plot_dir.mkdir(parents=True, exist_ok=True)
169
+
170
+ html_path = plot_dir.joinpath("code_size_evolution.html")
171
+ png_path = plot_dir.joinpath("code_size_evolution.png")
172
+
173
+ fig.write_html(html_path, include_plotlyjs="cdn")
174
+ fig.write_image(png_path, width=1200, height=700, scale=2)
175
+
176
+ print(f"🖼️ Interactive plot saved as {html_path}")
177
+ print(f"🖼️ Static image saved as {png_path}")
178
+ # Print statistics
179
+ print("\n📊 Repository Statistics:")
180
+ print(f"📚 Total commits analyzed: {len(df)}")
181
+ print(f"🔙 Initial line count: {df['lines'][-1]:,}")
182
+ print(f"🔜 Final line count: {df['lines'][0]:,}")
183
+ print(f"📈 Net change: {df['lines'][0] - df['lines'][-1]:,} lines")
184
+
185
+
186
+ def _print_python_files_by_size_impl(repo_path: str) -> "Union[pl.DataFrame, Exception]":
187
+ import polars as pl
188
+ import plotly.graph_objects as go
189
+ import plotly.express as px
190
+
191
+ try:
192
+ import os
193
+ if not os.path.exists(repo_path):
194
+ return ValueError(f"Repository path does not exist: {repo_path}")
195
+ # Initialize data storage
196
+ file_data: "List[Dict[str, Union[str, int]]]" = []
197
+
198
+ # Walk through the repository
199
+ for root, _, files in os.walk(repo_path):
200
+ # Skip .git directory and other hidden directories
201
+ if ".git" in Path(root).parts or any(part.startswith(".") for part in Path(root).parts):
202
+ continue
203
+
204
+ for file in files:
205
+ if file.endswith(".py"):
206
+ file_path = os.path.join(root, file)
207
+ try:
208
+ # Count lines in the file
209
+ with open(file_path, "r", encoding="utf-8", errors="replace") as f:
210
+ line_count = sum(1 for _ in f)
211
+
212
+ # Make path relative to repo_path for better display
213
+ rel_path = os.path.relpath(file_path, repo_path)
214
+ file_data.append({"filename": rel_path, "lines": line_count})
215
+ except Exception as e:
216
+ print(f"⚠️ Warning: Could not read {file_path}: {str(e)}")
217
+ continue
218
+
219
+ # Check if any files were found
220
+ if not file_data:
221
+ return ValueError("❌ No Python files found in the repository")
222
+
223
+ # Convert to DataFrame
224
+ df = pl.DataFrame(file_data)
225
+
226
+ # Sort DataFrame by line count (descending)
227
+ df = df.sort("lines", descending=True)
228
+ df = df.filter(pl.col("lines") > 0) # Filter out empty files
229
+
230
+ # Add total count
231
+ total_lines = int(df["lines"].sum())
232
+ file_count: int = len(df)
233
+
234
+ # Print the DataFrame
235
+ print("\n📊 Python Files Line Count (sorted max to min):")
236
+ print(df)
237
+ print(f"\n📁 Total Python files: {file_count}")
238
+ print(f"📝 Total lines of Python code: {total_lines:,}")
239
+
240
+ # Create visualizations with Plotly
241
+ # Only visualize top files (too many files make the chart unreadable)
242
+ top_n: int = min(20, len(df))
243
+ top_files_df = df.head(top_n).clone()
244
+
245
+ # Calculate percentage of total for top files
246
+ top_files_df = top_files_df.with_columns((pl.col("lines") / total_lines * 100).round(1).alias("percentage"))
247
+
248
+ # Shorten filenames for better display
249
+ import os
250
+
251
+ top_files_df = top_files_df.with_columns(pl.col("filename").map_elements(lambda x: os.path.basename(x) if len(x) > 25 else x, return_dtype=pl.Utf8).alias("short_name"))
252
+
253
+ # Create bar chart with hover info showing full path
254
+ fig = go.Figure()
255
+
256
+ # Add bars with gradient color based on line count
257
+ fig.add_trace(
258
+ go.Bar(
259
+ x=top_files_df["short_name"].to_list(),
260
+ y=top_files_df["lines"].to_list(),
261
+ text=[f"{x}%" for x in top_files_df["percentage"].to_list()],
262
+ textposition="auto",
263
+ hovertemplate="<b>%{customdata}</b><br>Lines: %{y:,}<br>Percentage: %{text}<extra></extra>",
264
+ customdata=top_files_df["filename"],
265
+ marker={"color": top_files_df["lines"], "colorscale": "Viridis", "showscale": True, "colorbar": {"title": "Lines", "thickness": 20, "tickformat": ","}, "line": {"width": 1, "color": "white"}},
266
+ opacity=0.9,
267
+ )
268
+ )
269
+
270
+ # Update layout with dark theme
271
+ fig.update_layout(
272
+ title={"text": f"🏆 Top {top_n} Python Files by Size", "y": 0.95, "x": 0.5, "xanchor": "center", "yanchor": "top", "font": {"size": 24, "color": "white"}},
273
+ xaxis_title="File Name 📄",
274
+ yaxis_title="Lines of Code 📝",
275
+ template="plotly_dark",
276
+ plot_bgcolor="rgba(25, 25, 35, 1)",
277
+ paper_bgcolor="rgba(15, 15, 25, 1)",
278
+ font={"family": "Arial, sans-serif", "size": 14, "color": "white"},
279
+ height=700,
280
+ margin={"l": 80, "r": 80, "t": 100, "b": 100},
281
+ xaxis={"tickangle": 45, "showgrid": False, "showline": True, "linecolor": "rgba(200, 200, 255, 0.2)", "tickfont": {"size": 12}},
282
+ yaxis={"showgrid": True, "gridcolor": "rgba(80, 80, 100, 0.2)", "showline": True, "linecolor": "rgba(200, 200, 255, 0.2)", "tickformat": ",", "tickfont": {"size": 12}},
283
+ )
284
+
285
+ # Define pie chart figure before conditionally using it
286
+ fig2: "Optional[go.Figure]" = None
287
+
288
+ # Add pie chart showing distribution
289
+ if len(df) > top_n:
290
+ # Prepare data for pie chart - top files plus "Others"
291
+ others_lines = df.slice(top_n)["lines"].sum()
292
+ pie_labels = list(top_files_df["short_name"]) + ["Others"]
293
+ pie_values = list(top_files_df["lines"]) + [others_lines]
294
+ pie_customdata = list(top_files_df["filename"]) + [f"Other {len(df) - top_n} files"]
295
+
296
+ fig2 = go.Figure()
297
+ fig2.add_trace(go.Pie(labels=pie_labels, values=pie_values, customdata=pie_customdata, textinfo="percent", hovertemplate="<b>%{customdata}</b><br>Lines: %{value:,}<br>Percentage: %{percent}<extra></extra>", marker={"colors": px.colors.sequential.Viridis, "line": {"color": "white", "width": 1}}, hole=0.4, sort=False))
298
+
299
+ fig2.update_layout(
300
+ title={"text": "🍩 Python Code Distribution by File", "y": 0.95, "x": 0.5, "xanchor": "center", "yanchor": "top", "font": {"size": 24, "color": "white"}},
301
+ template="plotly_dark",
302
+ plot_bgcolor="rgba(25, 25, 35, 1)",
303
+ paper_bgcolor="rgba(15, 15, 25, 1)",
304
+ font={"family": "Arial, sans-serif", "size": 14, "color": "white"},
305
+ height=700,
306
+ annotations=[{"text": f"Total<br>{total_lines:,}<br>lines", "x": 0.5, "y": 0.5, "font": {"size": 18, "color": "white"}, "showarrow": False}],
307
+ )
308
+
309
+ # Save visualizations
310
+ plot_dir = Path.home().joinpath("tmp_results", "tmp_images", Path(repo_path).name)
311
+ plot_dir.mkdir(parents=True, exist_ok=True)
312
+
313
+ # Bar chart
314
+ bar_html_path = plot_dir.joinpath("top_files_by_size.html")
315
+ bar_png_path = plot_dir.joinpath("top_files_by_size.png")
316
+ fig.write_html(bar_html_path, include_plotlyjs="cdn")
317
+ fig.write_image(bar_png_path, width=1200, height=700, scale=2)
318
+
319
+ print(f"\n🖼️ Interactive bar chart saved as {bar_html_path}")
320
+ print(f"🖼️ Static bar chart saved as {bar_png_path}")
321
+
322
+ # Pie chart if available
323
+ if fig2 is not None:
324
+ pie_html_path = plot_dir.joinpath("files_distribution_pie.html")
325
+ pie_png_path = plot_dir.joinpath("files_distribution_pie.png")
326
+ fig2.write_html(pie_html_path, include_plotlyjs="cdn")
327
+ fig2.write_image(pie_png_path, width=1200, height=700, scale=2)
328
+
329
+ print(f"🖼️ Interactive pie chart saved as {pie_html_path}")
330
+ print(f"🖼️ Static pie chart saved as {pie_png_path}")
331
+
332
+ return df
333
+
334
+ except Exception as e:
335
+ return Exception(f"❌ Error analyzing repository: {str(e)}")
336
+
337
+
338
+ @app.command()
339
+ def print_python_files_by_size(repo_path: Annotated[str, typer.Argument(..., help="Path to the git repository")]):
340
+ """Print Python files sorted by size with visualizations."""
341
+ result = _print_python_files_by_size_impl(repo_path)
342
+ if isinstance(result, Exception):
343
+ print(f"Error: {result}")
344
+ return
345
+
346
+
347
+ if __name__ == "__main__":
348
+ app()
@@ -0,0 +1,17 @@
1
+
2
+ import typer
3
+ from typing import Annotated
4
+
5
+
6
+ def analyze_repo_development(repo_path: Annotated[str, typer.Argument(..., help="Path to the git repository")]):
7
+ from machineconfig.scripts.python.helpers_repos import count_lines
8
+ from pathlib import Path
9
+ count_lines_path = Path(count_lines.__file__)
10
+ # --project $HOME/code/ machineconfig --group plot
11
+ cmd = f"""uv run --python 3.14 --with "machineconfig[plot]>=7.66" {count_lines_path} analyze-over-time {repo_path}"""
12
+ from machineconfig.utils.code import run_shell_script
13
+ run_shell_script(cmd)
14
+
15
+
16
+ if __name__ == "__main__":
17
+ pass
@@ -0,0 +1,77 @@
1
+
2
+
3
+
4
+ from typing import Optional
5
+ from pathlib import Path
6
+ from machineconfig.utils.source_of_truth import CONFIG_ROOT, DEFAULTS_PATH
7
+
8
+ import typer
9
+
10
+
11
+ def resolve_directory(directory: Optional[str]) -> Path:
12
+ if directory is None:
13
+ directory = Path.cwd().as_posix()
14
+ typer.echo(f"📁 Using directory: {directory}")
15
+ return Path(directory).expanduser().absolute()
16
+ def git_operations(
17
+ directory: Optional[str],
18
+ *,
19
+ pull: bool,
20
+ commit: bool,
21
+ push: bool,
22
+ recursive: bool,
23
+ auto_uv_sync: bool,
24
+ ) -> None:
25
+
26
+ repos_root = resolve_directory(directory)
27
+ from machineconfig.scripts.python.helpers_repos.action import perform_git_operations
28
+ from machineconfig.utils.path_extended import PathExtended
29
+ perform_git_operations(
30
+ repos_root=PathExtended(repos_root),
31
+ pull=pull,
32
+ commit=commit,
33
+ push=push,
34
+ recursive=recursive,
35
+ auto_uv_sync=auto_uv_sync,
36
+ )
37
+ def resolve_spec_path(directory: Optional[str], cloud: Optional[str]) -> Path:
38
+ repos_root = resolve_directory(directory)
39
+ from machineconfig.utils.path_extended import PathExtended
40
+ if not repos_root.exists() or repos_root.name != "repos.json":
41
+ relative_repos_root = PathExtended(repos_root).expanduser().absolute().relative_to(Path.home())
42
+ candidate = Path(CONFIG_ROOT).joinpath("repos").joinpath(relative_repos_root).joinpath("repos.json")
43
+ repos_root = candidate
44
+ if not repos_root.exists():
45
+ cloud_name: Optional[str]
46
+ if cloud is None:
47
+ from machineconfig.utils.io import read_ini
48
+ cloud_name = read_ini(DEFAULTS_PATH)["general"]["rclone_config_name"]
49
+ typer.echo(f"⚠️ Using default cloud: {cloud_name}")
50
+ else:
51
+ cloud_name = cloud
52
+ assert cloud_name is not None, (
53
+ f"Path {repos_root} does not exist and cloud was not passed. You can't clone without one of them."
54
+ )
55
+ from machineconfig.utils.path_extended import PathExtended
56
+ PathExtended(repos_root).from_cloud(cloud=cloud_name, rel2home=True)
57
+ assert repos_root.exists() and repos_root.name == "repos.json", (
58
+ f"Path {repos_root} does not exist and cloud was not passed. You can't clone without one of them."
59
+ )
60
+ return repos_root
61
+ def clone_from_specs(
62
+ directory: Optional[str],
63
+ cloud: Optional[str],
64
+ *,
65
+ checkout_branch_flag: bool,
66
+ checkout_commit_flag: bool,
67
+ ) -> None:
68
+
69
+ typer.echo("\n📥 Cloning or checking out repositories...")
70
+ spec_path = resolve_spec_path(directory, cloud)
71
+ from machineconfig.scripts.python.helpers_repos.clone import clone_repos
72
+ clone_repos(
73
+ spec_path=spec_path,
74
+ preferred_remote=None,
75
+ checkout_branch_flag=checkout_branch_flag,
76
+ checkout_commit_flag=checkout_commit_flag,
77
+ )