machineconfig 8.14__py3-none-any.whl → 8.45__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 (269) hide show
  1. machineconfig/cluster/remote/run_cluster.py +1 -1
  2. machineconfig/cluster/remote/run_remote.py +1 -1
  3. machineconfig/cluster/sessions_managers/utils/maker.py +10 -8
  4. machineconfig/cluster/sessions_managers/wt_local.py +1 -1
  5. machineconfig/cluster/sessions_managers/wt_local_manager.py +1 -1
  6. machineconfig/cluster/sessions_managers/zellij_local.py +1 -1
  7. machineconfig/cluster/sessions_managers/zellij_local_manager.py +1 -1
  8. machineconfig/jobs/installer/checks/check_installations.py +133 -0
  9. machineconfig/jobs/installer/checks/install_utils.py +132 -0
  10. machineconfig/jobs/installer/checks/report_utils.py +39 -0
  11. machineconfig/jobs/installer/checks/vt_utils.py +89 -0
  12. machineconfig/jobs/installer/installer_data.json +225 -140
  13. machineconfig/jobs/installer/linux_scripts/docker.sh +6 -9
  14. machineconfig/jobs/installer/package_groups.py +10 -9
  15. machineconfig/jobs/installer/python_scripts/boxes.py +1 -2
  16. machineconfig/jobs/installer/python_scripts/code.py +10 -8
  17. machineconfig/jobs/installer/python_scripts/hx.py +30 -13
  18. machineconfig/jobs/installer/python_scripts/nerfont_windows_helper.py +6 -5
  19. machineconfig/jobs/installer/python_scripts/sysabc.py +25 -19
  20. machineconfig/jobs/installer/python_scripts/yazi.py +33 -17
  21. machineconfig/jobs/scripts/powershell_scripts/cmatrix.ps1 +52 -0
  22. machineconfig/jobs/scripts/powershell_scripts/mount_ssh.ps1 +1 -1
  23. machineconfig/jobs/scripts_dynamic/a.py +413 -10
  24. machineconfig/profile/create_links.py +77 -20
  25. machineconfig/profile/create_links_export.py +40 -51
  26. machineconfig/profile/mapper_data.toml +30 -0
  27. machineconfig/profile/mapper_dotfiles.toml +253 -0
  28. machineconfig/scripts/python/agents.py +70 -172
  29. machineconfig/scripts/python/ai/initai.py +3 -1
  30. machineconfig/scripts/python/ai/scripts/__init__.py +1 -0
  31. machineconfig/scripts/python/ai/scripts/lint_and_type_check.ps1 +2 -0
  32. machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +7 -5
  33. machineconfig/scripts/python/ai/solutions/claude/claude.py +1 -1
  34. machineconfig/scripts/python/ai/solutions/cline/cline.py +1 -1
  35. machineconfig/scripts/python/ai/solutions/copilot/github_copilot.py +1 -1
  36. machineconfig/scripts/python/ai/solutions/copilot/instructions/python/dev.instructions.md +29 -0
  37. machineconfig/scripts/python/ai/solutions/crush/crush.py +1 -1
  38. machineconfig/scripts/python/ai/solutions/cursor/cursors.py +1 -1
  39. machineconfig/scripts/python/ai/solutions/gemini/gemini.py +1 -1
  40. machineconfig/scripts/python/ai/solutions/gemini/settings.json +3 -0
  41. machineconfig/scripts/python/ai/{solutions → utils}/generic.py +2 -15
  42. machineconfig/scripts/python/ai/utils/vscode_tasks.py +6 -3
  43. machineconfig/scripts/python/cloud.py +58 -11
  44. machineconfig/scripts/python/croshell.py +4 -156
  45. machineconfig/scripts/python/devops.py +57 -40
  46. machineconfig/scripts/python/devops_navigator.py +17 -3
  47. machineconfig/scripts/python/fire_jobs.py +8 -207
  48. machineconfig/scripts/python/ftpx.py +5 -225
  49. machineconfig/scripts/python/graph/cli_graph.json +8743 -0
  50. machineconfig/scripts/python/{env_manager → helper_env}/path_manager_tui.py +2 -2
  51. machineconfig/scripts/python/{env_manager → helpers/helper_env}/env_manager_tui.py +1 -1
  52. machineconfig/scripts/python/helpers/helper_env/path_manager_tui.py +228 -0
  53. machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_crush.py +1 -1
  54. machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_cursor_agents.py +1 -1
  55. machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_gemini.py +1 -1
  56. machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_qwen.py +1 -1
  57. machineconfig/scripts/python/helpers/helpers_agents/agents_impl.py +168 -0
  58. machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/fire_agents_help_launch.py +5 -5
  59. machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_copy.py +6 -6
  60. machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_mount.py +10 -5
  61. machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_sync.py +3 -3
  62. machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/helpers2.py +1 -1
  63. machineconfig/scripts/python/helpers/helpers_croshell/croshell_impl.py +225 -0
  64. machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/scheduler.py +4 -4
  65. machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/start_slidev.py +7 -6
  66. machineconfig/scripts/python/helpers/helpers_devops/backup_config.py +149 -0
  67. machineconfig/scripts/python/helpers/helpers_devops/cli_backup_retrieve.py +267 -0
  68. machineconfig/scripts/python/helpers/helpers_devops/cli_config.py +98 -0
  69. machineconfig/scripts/python/helpers/helpers_devops/cli_config_dotfile.py +274 -0
  70. machineconfig/scripts/python/helpers/helpers_devops/cli_data.py +76 -0
  71. machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_nw.py +52 -72
  72. machineconfig/scripts/python/helpers/helpers_devops/cli_repos.py +265 -0
  73. machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_self.py +39 -23
  74. machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_share_file.py +44 -30
  75. machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_share_server.py +26 -43
  76. machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_share_terminal.py +12 -6
  77. machineconfig/scripts/python/helpers/helpers_devops/cli_ssh.py +167 -0
  78. machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/devops_status.py +12 -6
  79. machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/devops_update_repos.py +1 -1
  80. machineconfig/scripts/python/{interactive.py → helpers/helpers_devops/interactive.py} +68 -52
  81. machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/run_script.py +75 -58
  82. machineconfig/scripts/python/helpers/helpers_devops/themes/choose_starship_theme.ps1 +41 -0
  83. machineconfig/scripts/python/helpers/helpers_devops/themes/choose_starship_theme.sh +48 -0
  84. machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/themes/choose_wezterm_theme.py +3 -3
  85. machineconfig/scripts/python/helpers/helpers_fire_command/fire_jobs_impl.py +233 -0
  86. machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/fire_jobs_route_helper.py +3 -3
  87. machineconfig/scripts/python/helpers/helpers_msearch/msearch_impl.py +248 -0
  88. machineconfig/scripts/python/{helpers_msearch → helpers/helpers_msearch}/scripts_linux/fzfg +4 -3
  89. machineconfig/scripts/python/helpers/helpers_msearch/scripts_linux/search_with_context.sh +48 -0
  90. machineconfig/scripts/python/{helpers_msearch → helpers/helpers_msearch}/scripts_windows/fzfg.ps1 +1 -1
  91. machineconfig/scripts/python/helpers/helpers_navigator/__init__.py +20 -0
  92. machineconfig/scripts/python/helpers/helpers_navigator/cli_graph_loader.py +234 -0
  93. machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/command_builder.py +61 -13
  94. machineconfig/scripts/python/helpers/helpers_navigator/command_detail.py +153 -0
  95. machineconfig/scripts/python/helpers/helpers_navigator/command_tree.py +45 -0
  96. machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/data_models.py +18 -11
  97. machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/main_app.py +5 -5
  98. machineconfig/scripts/python/helpers/helpers_network/__init__.py +0 -0
  99. machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/address.py +15 -17
  100. machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/address_switch.py +1 -1
  101. machineconfig/scripts/python/helpers/helpers_network/ftpx_impl.py +276 -0
  102. machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/mount_ssh.py +2 -2
  103. machineconfig/scripts/python/helpers/helpers_network/ssh_add_identity.py +73 -0
  104. machineconfig/scripts/python/helpers/helpers_network/ssh_add_ssh_key.py +175 -0
  105. machineconfig/scripts/python/helpers/helpers_network/ssh_debug_linux.py +319 -0
  106. machineconfig/scripts/python/helpers/helpers_network/ssh_debug_windows.py +275 -0
  107. machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/action.py +3 -3
  108. machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/action_helper.py +3 -3
  109. machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/cloud_repo_sync.py +116 -33
  110. machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/grource.py +3 -2
  111. machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/record.py +33 -13
  112. machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/repo_analyzer_2.py +63 -19
  113. machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/update.py +0 -6
  114. machineconfig/scripts/python/helpers/helpers_search/script_help.py +81 -0
  115. machineconfig/scripts/python/helpers/helpers_sessions/__init__.py +0 -0
  116. machineconfig/scripts/python/helpers/helpers_sessions/sessions_impl.py +177 -0
  117. machineconfig/scripts/python/{helpers_sessions → helpers/helpers_sessions}/sessions_multiprocess.py +1 -1
  118. machineconfig/scripts/python/helpers/helpers_terminal/__init__.py +0 -0
  119. machineconfig/scripts/python/helpers/helpers_terminal/terminal_impl.py +96 -0
  120. machineconfig/scripts/python/{helpers_utils → helpers/helpers_utils}/download.py +1 -1
  121. machineconfig/scripts/python/{helpers_utils → helpers/helpers_utils}/python.py +46 -26
  122. machineconfig/scripts/python/helpers/helpers_utils/specs.py +246 -0
  123. machineconfig/scripts/python/mcfg_entry.py +133 -48
  124. machineconfig/scripts/python/msearch.py +15 -61
  125. machineconfig/scripts/python/sessions.py +59 -194
  126. machineconfig/scripts/python/terminal.py +18 -96
  127. machineconfig/scripts/python/utils.py +101 -20
  128. machineconfig/settings/atuin/config.toml +294 -0
  129. machineconfig/settings/atuin/themes/catppuccin-mocha-mauve.toml +12 -0
  130. machineconfig/settings/linters/.ruff.toml +1 -0
  131. machineconfig/settings/mprocs/windows/mprocs.yaml +2 -2
  132. machineconfig/settings/shells/bash/init.sh +6 -3
  133. machineconfig/settings/shells/pwsh/init.ps1 +69 -1
  134. machineconfig/settings/shells/pwsh/search_pwsh_history.ps1 +99 -0
  135. machineconfig/settings/shells/wezterm/wezterm.lua +4 -1
  136. machineconfig/settings/shells/wt/settings.json +20 -7
  137. machineconfig/settings/shells/zsh/init.sh +34 -4
  138. machineconfig/settings/television/cable_unix/bash-history.toml +1 -1
  139. machineconfig/settings/television/cable_windows/pwsh-history.toml +1 -1
  140. machineconfig/settings/tv/config.toml +234 -0
  141. machineconfig/settings/tv/themes/catppuccin-mocha-sky.toml +22 -0
  142. machineconfig/settings/wsl/.wslconfig +5 -30
  143. machineconfig/settings/yazi/yazi_linux.toml +18 -8
  144. machineconfig/settings/zellij/layouts/st.kdl +2 -2
  145. machineconfig/settings/zellij/layouts/st2.kdl +1 -1
  146. machineconfig/setup_linux/web_shortcuts/interactive.sh +10 -10
  147. machineconfig/setup_linux/web_shortcuts/live_from_github.sh +3 -0
  148. machineconfig/setup_mac/__init__.py +0 -2
  149. machineconfig/setup_windows/__init__.py +0 -1
  150. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +14 -13
  151. machineconfig/setup_windows/web_shortcuts/live_from_github.ps1 +4 -3
  152. machineconfig/setup_windows/web_shortcuts/quick_init.ps1 +3 -3
  153. machineconfig/type_hinting/sql/__init__.py +1 -0
  154. machineconfig/type_hinting/sql/base.py +216 -0
  155. machineconfig/type_hinting/sql/core_schema.py +64 -0
  156. machineconfig/type_hinting/sql/core_schema_typeddict.py +41 -0
  157. machineconfig/type_hinting/sql/typeddict_codegen.py +222 -0
  158. machineconfig/type_hinting/typedict/__init__.py +1 -0
  159. machineconfig/type_hinting/typedict/ast_utils.py +130 -0
  160. machineconfig/type_hinting/typedict/generator_helpers.py +319 -0
  161. machineconfig/type_hinting/typedict/generators.py +231 -0
  162. machineconfig/type_hinting/typedict/polars_schema.py +24 -0
  163. machineconfig/type_hinting/typedict/polars_schema_typeddict.py +63 -0
  164. machineconfig/utils/accessories.py +24 -0
  165. machineconfig/utils/code.py +41 -13
  166. machineconfig/utils/files/ascii_art.py +10 -14
  167. machineconfig/utils/files/headers.py +3 -5
  168. machineconfig/utils/files/read.py +8 -1
  169. machineconfig/utils/installer_utils/github_release_bulk.py +1 -0
  170. machineconfig/utils/installer_utils/install_from_url.py +1 -1
  171. machineconfig/utils/installer_utils/installer_class.py +12 -4
  172. machineconfig/utils/installer_utils/installer_cli.py +1 -15
  173. machineconfig/utils/installer_utils/installer_helper.py +2 -2
  174. machineconfig/utils/installer_utils/installer_locator_utils.py +13 -13
  175. machineconfig/utils/installer_utils/installer_runner.py +4 -4
  176. machineconfig/utils/meta.py +6 -4
  177. machineconfig/utils/options.py +49 -19
  178. machineconfig/utils/options_utils/__init__.py +0 -0
  179. machineconfig/utils/options_utils/options_tv_linux.py +211 -0
  180. machineconfig/utils/options_utils/options_tv_windows.py +88 -0
  181. machineconfig/utils/options_utils/tv_options.py +37 -0
  182. machineconfig/utils/path_extended.py +6 -6
  183. machineconfig/utils/scheduler.py +8 -2
  184. machineconfig/utils/schemas/fire_agents/fire_agents_input.py +1 -1
  185. machineconfig/utils/source_of_truth.py +6 -1
  186. machineconfig/utils/ssh.py +69 -18
  187. machineconfig/utils/ssh_utils/abc.py +1 -1
  188. machineconfig/utils/ssh_utils/wsl.py +107 -170
  189. machineconfig/utils/ssh_utils/wsl_helper.py +217 -0
  190. machineconfig/utils/upgrade_packages.py +4 -8
  191. {machineconfig-8.14.dist-info → machineconfig-8.45.dist-info}/METADATA +29 -22
  192. {machineconfig-8.14.dist-info → machineconfig-8.45.dist-info}/RECORD +247 -208
  193. machineconfig/jobs/installer/check_installations.py +0 -248
  194. machineconfig/profile/backup.toml +0 -49
  195. machineconfig/profile/mapper.toml +0 -263
  196. machineconfig/scripts/python/helpers_devops/cli_config.py +0 -105
  197. machineconfig/scripts/python/helpers_devops/cli_config_dotfile.py +0 -89
  198. machineconfig/scripts/python/helpers_devops/cli_data.py +0 -25
  199. machineconfig/scripts/python/helpers_devops/cli_repos.py +0 -208
  200. machineconfig/scripts/python/helpers_devops/devops_backup_retrieve.py +0 -80
  201. machineconfig/scripts/python/helpers_devops/themes/choose_starship_theme.bash +0 -3
  202. machineconfig/scripts/python/helpers_navigator/__init__.py +0 -20
  203. machineconfig/scripts/python/helpers_navigator/command_detail.py +0 -44
  204. machineconfig/scripts/python/helpers_navigator/command_tree.py +0 -620
  205. machineconfig/scripts/python/helpers_network/ssh_add_identity.py +0 -116
  206. machineconfig/scripts/python/helpers_network/ssh_add_ssh_key.py +0 -153
  207. machineconfig/scripts/python/helpers_network/ssh_debug_linux.py +0 -391
  208. machineconfig/scripts/python/helpers_network/ssh_debug_windows.py +0 -338
  209. machineconfig/scripts/python/helpers_repos/entrypoint.py +0 -77
  210. machineconfig/setup_mac/ssh/openssh_setup.sh +0 -114
  211. machineconfig/setup_windows/ssh/add-sshkey.ps1 +0 -29
  212. machineconfig/setup_windows/ssh/openssh-server.ps1 +0 -37
  213. machineconfig/utils/options_tv.py +0 -119
  214. machineconfig/utils/tst.py +0 -20
  215. /machineconfig/{scripts/python/helpers_agents → jobs/installer/checks}/__init__.py +0 -0
  216. /machineconfig/scripts/python/ai/{solutions/_shared.py → utils/shared.py} +0 -0
  217. /machineconfig/scripts/python/{helpers_agents/agentic_frameworks → graph}/__init__.py +0 -0
  218. /machineconfig/scripts/python/{helpers_cloud → helpers}/__init__.py +0 -0
  219. /machineconfig/scripts/python/{env_manager → helpers/helper_env}/__init__.py +0 -0
  220. /machineconfig/scripts/python/{env_manager → helpers/helper_env}/path_manager_backend.py +0 -0
  221. /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_agents}/__init__.py +0 -0
  222. /machineconfig/scripts/python/{helpers_devops → helpers/helpers_agents/agentic_frameworks}/__init__.py +0 -0
  223. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_crush.json +0 -0
  224. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/fire_agents_help_search.py +0 -0
  225. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/fire_agents_helper_types.py +0 -0
  226. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/fire_agents_load_balancer.py +0 -0
  227. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/configs/aichat/config.yaml +0 -0
  228. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/configs/aider/.aider.conf.yml +0 -0
  229. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/configs/copilot/config.yml +0 -0
  230. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/configs/crush/crush.json +0 -0
  231. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/configs/gemini/settings.json +0 -0
  232. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/privacy.py +0 -0
  233. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/templates/prompt.txt +0 -0
  234. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/templates/template.ps1 +0 -0
  235. /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/templates/template.sh +0 -0
  236. /machineconfig/scripts/python/{helpers_devops/themes → helpers/helpers_cloud}/__init__.py +0 -0
  237. /machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_helpers.py +0 -0
  238. /machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/helpers5.py +0 -0
  239. /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_croshell}/__init__.py +0 -0
  240. /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/crosh.py +0 -0
  241. /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/pomodoro.py +0 -0
  242. /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/viewer.py +0 -0
  243. /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/viewer_template.py +0 -0
  244. /machineconfig/scripts/python/{helpers_network → helpers/helpers_devops}/__init__.py +0 -0
  245. /machineconfig/scripts/python/{helpers_sessions → helpers/helpers_devops/themes}/__init__.py +0 -0
  246. /machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/themes/choose_pwsh_theme.ps1 +0 -0
  247. /machineconfig/scripts/python/{helpers_devops/themes/choose_starship_theme.ps1 → helpers/helpers_fire_command/__init__.py} +0 -0
  248. /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/cloud_manager.py +0 -0
  249. /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/f.py +0 -0
  250. /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/file_wrangler.py +0 -0
  251. /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/fire_jobs_args_helper.py +0 -0
  252. /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/fire_jobs_streamlit_helper.py +0 -0
  253. /machineconfig/scripts/python/{helpers_msearch → helpers/helpers_msearch}/__init__.py +0 -0
  254. /machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/search_bar.py +0 -0
  255. /machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/mount_nfs.py +0 -0
  256. /machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/mount_nw_drive.py +0 -0
  257. /machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/onetimeshare.py +0 -0
  258. /machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/wifi_conn.py +0 -0
  259. /machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/clone.py +0 -0
  260. /machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/repo_analyzer_1.py +0 -0
  261. /machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/sync.py +0 -0
  262. /machineconfig/scripts/python/helpers/{ast_search.py → helpers_search/ast_search.py} +0 -0
  263. /machineconfig/scripts/python/helpers/{qr_code.py → helpers_search/qr_code.py} +0 -0
  264. /machineconfig/scripts/python/helpers/{repo_rag.py → helpers_search/repo_rag.py} +0 -0
  265. /machineconfig/scripts/python/helpers/{symantic_search.py → helpers_search/symantic_search.py} +0 -0
  266. /machineconfig/scripts/python/{helpers_utils → helpers/helpers_utils}/pdf.py +0 -0
  267. {machineconfig-8.14.dist-info → machineconfig-8.45.dist-info}/WHEEL +0 -0
  268. {machineconfig-8.14.dist-info → machineconfig-8.45.dist-info}/entry_points.txt +0 -0
  269. {machineconfig-8.14.dist-info → machineconfig-8.45.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,177 @@
1
+ """Pure Python implementations for sessions commands - no typer dependencies."""
2
+
3
+ from typing import Optional, Literal
4
+ from pathlib import Path
5
+
6
+
7
+ def balance_load(
8
+ layout_path: str,
9
+ max_thresh: int,
10
+ thresh_type: Literal["number", "n", "weight", "w"],
11
+ breaking_method: Literal["moreLayouts", "ml", "combineTabs", "ct"],
12
+ output_path: Optional[str],
13
+ ) -> None:
14
+ """Adjust layout file to limit max tabs per layout, etc."""
15
+ thresh_type_resolved: dict[str, Literal["number", "weight"]] = {"number": "number", "n": "number", "weight": "weight", "w": "weight"}
16
+ breaking_method_resolved: dict[str, Literal["moreLayouts", "combineTabs"]] = {"moreLayouts": "moreLayouts", "ml": "moreLayouts", "combineTabs": "combineTabs", "ct": "combineTabs"}
17
+
18
+ layout_path_obj = Path(layout_path).expanduser().absolute()
19
+
20
+ from machineconfig.utils.schemas.layouts.layout_types import LayoutsFile
21
+ import json
22
+ layoutfile: LayoutsFile = json.loads(layout_path_obj.read_text())
23
+ layout_configs = layoutfile["layouts"]
24
+ from machineconfig.cluster.sessions_managers.utils.load_balancer import limit_tab_num
25
+ new_layouts = limit_tab_num(layout_configs=layout_configs, max_thresh=max_thresh, threshold_type=thresh_type_resolved[thresh_type], breaking_method=breaking_method_resolved[breaking_method])
26
+ layoutfile["layouts"] = new_layouts
27
+ target_file = Path(output_path) if output_path is not None else layout_path_obj.parent / f"{layout_path_obj.stem}_adjusted_{max_thresh}_{thresh_type}_{breaking_method}.json"
28
+ target_file.parent.mkdir(parents=True, exist_ok=True)
29
+ target_file.write_text(data=json.dumps(layoutfile, indent=4), encoding="utf-8")
30
+ print(f"Adjusted layout saved to {target_file}")
31
+
32
+
33
+ def select_layout(layouts_json_file: str, selected_layouts_names: Optional[list[str]], select_interactively: bool, subsitute_home: bool) -> list["LayoutConfig"]: # type: ignore[name-defined]
34
+ """Select layout(s) from a layout file."""
35
+ import json
36
+ from machineconfig.utils.schemas.layouts.layout_types import LayoutsFile, LayoutConfig
37
+ json_str = Path(layouts_json_file).read_text(encoding="utf-8")
38
+ if subsitute_home:
39
+ json_str = json_str.replace("~", str(Path.home())).replace("$HOME", str(Path.home()))
40
+ json_str = json_str.replace("""Command": "f """, """Command": "~/.config/machineconfig/scripts/wrap_mcfg fire """)
41
+ json_str = json_str.replace("""Command": "s """, """Command": "~/.config/machineconfig/scripts/wrap_mcfg sessions """)
42
+
43
+ layout_file: LayoutsFile = json.loads(json_str)
44
+ if len(layout_file["layouts"]) == 0:
45
+ raise ValueError(f"No layouts found in {layouts_json_file}")
46
+ if selected_layouts_names is None:
47
+ if not select_interactively:
48
+ return layout_file["layouts"]
49
+ options = [layout["layoutName"] for layout in layout_file["layouts"]]
50
+ from machineconfig.utils.options import choose_from_options
51
+ selected_layouts_names = choose_from_options(multi=True, options=options, prompt="Choose a layout configuration:", tv=True, msg="Choose one option")
52
+ print(f"Selected layout(s): {selected_layouts_names}")
53
+ layouts_chosen: list[LayoutConfig] = []
54
+ for name in selected_layouts_names:
55
+ layout_chosen = next((layout for layout in layout_file["layouts"] if layout["layoutName"] == name), None)
56
+ if layout_chosen is None:
57
+ layout_chosen = next((layout for layout in layout_file["layouts"] if layout["layoutName"].lower() == name.lower()), None)
58
+ if layout_chosen is None:
59
+ available_layouts = [layout["layoutName"] for layout in layout_file["layouts"]]
60
+ raise ValueError(f"Layout '{name}' not found. Available layouts: {available_layouts}")
61
+ layouts_chosen.append(layout_chosen)
62
+ return layouts_chosen
63
+
64
+
65
+ def find_layout_file(layout_path: str) -> str:
66
+ """Find layout file from a path."""
67
+ from machineconfig.utils.path_helper import search_for_files_of_interest, match_file_name, sanitize_path
68
+ from machineconfig.utils.options import choose_from_options
69
+ path_obj = sanitize_path(layout_path)
70
+ if not path_obj.exists():
71
+ choice_file = match_file_name(sub_string=layout_path, search_root=Path.cwd(), suffixes={".json"})
72
+ elif path_obj.is_dir():
73
+ print(f"🔍 Searching recursively for Python, PowerShell and Shell scripts in directory `{path_obj}`")
74
+ files = search_for_files_of_interest(path_obj, suffixes={".py", ".sh", ".ps1"})
75
+ print(f"🔍 Got #{len(files)} results.")
76
+ choice_file = choose_from_options(multi=False, options=files, tv=True, msg="Choose one option")
77
+ choice_file = Path(choice_file).expanduser().absolute()
78
+ else:
79
+ choice_file = path_obj
80
+ return str(choice_file)
81
+
82
+
83
+ def run_layouts(
84
+ layout_path: Optional[str],
85
+ max_tabs: int,
86
+ max_layouts: int,
87
+ sleep_inbetween: float,
88
+ monitor: bool,
89
+ parallel: bool,
90
+ kill_upon_completion: bool,
91
+ choose: Optional[str],
92
+ choose_interactively: bool,
93
+ subsitute_home: bool,
94
+ ) -> None:
95
+ """Launch terminal sessions based on a layout configuration file."""
96
+ if layout_path is None:
97
+ raise ValueError("layout_path is required")
98
+
99
+ layout_path_resolved = find_layout_file(layout_path=layout_path)
100
+ layouts_selected = select_layout(layouts_json_file=layout_path_resolved, selected_layouts_names=choose.split(",") if choose else None, select_interactively=choose_interactively, subsitute_home=subsitute_home)
101
+
102
+ if parallel and len(layouts_selected) > max_layouts:
103
+ raise ValueError(f"Number of layouts {len(layouts_selected)} exceeds the maximum allowed {max_layouts}. Please adjust your layout file.")
104
+ for a_layout in layouts_selected:
105
+ if len(a_layout["layoutTabs"]) > max_tabs:
106
+ raise ValueError(f"Layout '{a_layout.get('layoutName', 'Unnamed')}' has {len(a_layout['layoutTabs'])} tabs which exceeds the max of {max_tabs}.")
107
+
108
+ import time
109
+ import platform
110
+ if platform.system() == "Linux" or platform.system() == "Darwin":
111
+ from machineconfig.cluster.sessions_managers.zellij_local_manager import ZellijLocalManager
112
+ if not parallel:
113
+ iterable = [[item] for item in layouts_selected]
114
+ else:
115
+ iterable = [layouts_selected]
116
+ for i, a_layouts in enumerate(iterable):
117
+ manager = ZellijLocalManager(session_layouts=a_layouts)
118
+ manager.start_all_sessions(poll_interval=2, poll_seconds=2)
119
+ if monitor:
120
+ manager.run_monitoring_routine(wait_ms=2000)
121
+ if kill_upon_completion:
122
+ manager.kill_all_sessions()
123
+ if i < len(layouts_selected) - 1:
124
+ time.sleep(sleep_inbetween)
125
+ elif platform.system() == "Windows":
126
+ from machineconfig.cluster.sessions_managers.wt_local_manager import WTLocalManager
127
+ if not parallel:
128
+ iterable = [[item] for item in layouts_selected]
129
+ else:
130
+ iterable = [layouts_selected]
131
+ for i, a_layouts in enumerate(iterable):
132
+ manager = WTLocalManager(session_layouts=a_layouts)
133
+ manager.start_all_sessions()
134
+ if monitor:
135
+ manager.run_monitoring_routine(wait_ms=2000)
136
+ if kill_upon_completion:
137
+ manager.kill_all_sessions()
138
+ if i < len(layouts_selected) - 1:
139
+ time.sleep(sleep_inbetween)
140
+ else:
141
+ print(f"❌ Unsupported platform: {platform.system()}")
142
+
143
+
144
+ def create_template(name: Optional[str], num_tabs: int) -> None:
145
+ """Create a layout template file."""
146
+ from machineconfig.utils.schemas.layouts.layout_types import LayoutsFile, TabConfig, LayoutConfig
147
+ tabs: list[TabConfig] = []
148
+ for i in range(1, num_tabs + 1):
149
+ tab: TabConfig = {
150
+ "tabName": f"Tab{i}",
151
+ "startDir": "~/" + str(Path.cwd().relative_to(Path.home())),
152
+ "command": "bash",
153
+ }
154
+ tabs.append(tab)
155
+ layouts: list[LayoutConfig] = [
156
+ {
157
+ "layoutName": f"{Path.cwd().name}Layout",
158
+ "layoutTabs": tabs,
159
+ }
160
+ ]
161
+ file: LayoutsFile = {
162
+ "$schema": "https://bit.ly/cfglayout", # type: ignore
163
+ "version": "0.1",
164
+ "layouts": layouts,
165
+ }
166
+ import json
167
+ json_string = json.dumps(file, indent=4)
168
+ if name is None:
169
+ layout_path = Path.cwd() / "layout.json"
170
+ else:
171
+ layout_path = Path.cwd() / (name.replace(".json", "") + ".json")
172
+ layout_path.parent.mkdir(parents=True, exist_ok=True)
173
+ if layout_path.exists():
174
+ print(f"❌ File {layout_path} already exists. Aborting to avoid overwriting.")
175
+ return
176
+ layout_path.write_text(json_string, encoding="utf-8")
177
+ print(f"✅ Created layout template at {layout_path}")
@@ -45,7 +45,7 @@ def create_from_function(
45
45
 
46
46
  # ========================= choosing function to run
47
47
  if function is None or function.strip() == "":
48
- from machineconfig.scripts.python.helpers_fire_command.fire_jobs_route_helper import choose_function_or_lines
48
+ from machineconfig.scripts.python.helpers.helpers_fire_command.fire_jobs_route_helper import choose_function_or_lines
49
49
  choice_function, choice_file, _kwargs_dict = choose_function_or_lines(choice_file, kwargs_dict={})
50
50
  else:
51
51
  choice_function = function
@@ -0,0 +1,96 @@
1
+ import re
2
+ import subprocess
3
+ from pathlib import Path
4
+
5
+
6
+ _ANSI_ESCAPE_RE = re.compile(
7
+ r"(?:\x1B|\u001B|\033)\[[0-?]*[ -/]*[@-~]|\[[0-9;?]+[ -/]*[@-~]|\[m"
8
+ )
9
+
10
+
11
+ def strip_ansi_codes(text: str) -> str:
12
+ return _ANSI_ESCAPE_RE.sub("", text)
13
+
14
+
15
+ def choose_zellij_session(name: str | None, new_session: bool, kill_all: bool) -> tuple[str, str | None]:
16
+ """Choose a Zellij session. Returns tuple of (action, script_to_run) where action is 'run_script', 'exit', or 'error'."""
17
+ if name is not None:
18
+ return ("run_script", f"zellij attach {name}")
19
+ if new_session:
20
+ cmd = "zellij --layout st2"
21
+ if kill_all:
22
+ cmd = f"zellij kill-all-sessions --yes\n{cmd}"
23
+ return ("run_script", cmd)
24
+ cmd = "zellij list-sessions"
25
+ try:
26
+ sessions: list[str] = subprocess.check_output(cmd, shell=True).decode().strip().split("\n")
27
+ except subprocess.CalledProcessError:
28
+ sessions = []
29
+ sessions = [s for s in sessions if s.strip()]
30
+ # print(f"Found Zellij sessions: {sessions}")
31
+ sessions.sort(key=lambda s: "EXITED" in s)
32
+ if "current" in sessions:
33
+ return ("error", "Already in a Zellij session, avoiding nesting and exiting.")
34
+ if len(sessions) == 0:
35
+ return ("run_script", "zellij --layout st2")
36
+ if len(sessions) == 1:
37
+ sn = strip_ansi_codes(sessions[0])
38
+ session_name = sn.split(" [Created")[0]
39
+ return ("run_script", f"zellij attach {session_name}")
40
+ from machineconfig.utils.options import choose_from_options
41
+ NEW_SESSION_LABEL = "NEW SESSION"
42
+ KILL_ALL_AND_NEW_LABEL = "KILL ALL SESSIONS & START NEW"
43
+ options = sessions + [NEW_SESSION_LABEL, KILL_ALL_AND_NEW_LABEL]
44
+ try:
45
+ session_name = choose_from_options(msg="Choose a Zellij session to attach to:", multi=False, options=options, tv=True)
46
+ except Exception as e:
47
+ return ("error", f"Error choosing Zellij session: {e}")
48
+ if session_name == NEW_SESSION_LABEL:
49
+ cmd = "zellij --layout st2"
50
+ if kill_all:
51
+ cmd = f"zellij kill-all-sessions --yes\n{cmd}"
52
+ return ("run_script", cmd)
53
+ if session_name == KILL_ALL_AND_NEW_LABEL:
54
+ return ("run_script", "zellij kill-all-sessions --yes\nzellij --layout st2")
55
+ session_name_clean = strip_ansi_codes(session_name)
56
+ session_name_clean = session_name_clean.split(" [Created")[0]
57
+ return ("run_script", f"zellij attach {session_name_clean}")
58
+
59
+
60
+ def get_session_tabs() -> list[tuple[str, str]]:
61
+ cmd = "zellij list-sessions"
62
+ try:
63
+ sessions: list[str] = subprocess.check_output(cmd, shell=True).decode().strip().split("\n")
64
+ except subprocess.CalledProcessError:
65
+ sessions = []
66
+ sessions = [strip_ansi_codes(s) for s in sessions]
67
+ active_sessions = [s for s in sessions if "EXITED" not in s]
68
+ result: list[tuple[str, str]] = []
69
+ for session_line in active_sessions:
70
+ session_name = session_line.split(" [Created")[0].strip()
71
+ tab_cmd = f"zellij --session {session_name} action query-tab-names"
72
+ try:
73
+ tabs: list[str] = subprocess.check_output(tab_cmd, shell=True).decode().strip().split("\n")
74
+ for tab in tabs:
75
+ if tab.strip():
76
+ result.append((session_name, tab.strip()))
77
+ except subprocess.CalledProcessError:
78
+ continue
79
+ return result
80
+
81
+
82
+ def start_wt(layout_name: str) -> tuple[str, str | None]:
83
+ """Start a Windows Terminal layout by name. Returns tuple of (status, message) where status is 'success' or 'error'."""
84
+ import json
85
+ from machineconfig.utils.schemas.layouts.layout_types import LayoutsFile
86
+ from machineconfig.cluster.sessions_managers.wt_local import run_wt_layout
87
+ layouts_file = Path.home().joinpath("dotfiles/machineconfig/layouts.json")
88
+ if not layouts_file.exists():
89
+ return ("error", f"❌ Layouts file not found: {layouts_file}")
90
+ layouts_data: LayoutsFile = json.loads(layouts_file.read_text(encoding="utf-8"))
91
+ chosen_layout = next((a_layout for a_layout in layouts_data["layouts"] if a_layout["layoutName"] == layout_name), None)
92
+ if not chosen_layout:
93
+ available_layouts = [a_layout["layoutName"] for a_layout in layouts_data["layouts"]]
94
+ return ("error", f"❌ Layout '{layout_name}' not found in layouts file.\nAvailable layouts: {', '.join(available_layouts)}")
95
+ run_wt_layout(layout_config=chosen_layout)
96
+ return ("success", None)
@@ -22,7 +22,7 @@ def download(
22
22
  if output is not None and output_dir is not None:
23
23
  typer.echo("❌ Error: --output and --output-dir cannot be used together.", err=True)
24
24
  return None
25
- typer.echo(f"📥 Downloading from: {url}")
25
+ typer.echo(f" {url}")
26
26
 
27
27
  def _sanitize_candidate_filename(name: str) -> Optional[str]:
28
28
  candidate = Path(name).name.strip()
@@ -1,11 +1,10 @@
1
- from tempfile import tempdir
2
1
  import typer
3
2
  from typing import Optional, Annotated, Literal, TypedDict, cast
4
3
 
5
4
 
6
5
  def tui_env(which: Annotated[Literal["PATH", "p", "ENV", "e"], typer.Argument(help="Which environment variable to display.")] = "ENV") -> None:
7
6
  """📚 NAVIGATE PATH variable with TUI"""
8
- from machineconfig.scripts.python import env_manager as navigator
7
+ from machineconfig.scripts.python.helpers import helper_env as navigator
9
8
  from pathlib import Path
10
9
 
11
10
  match which:
@@ -18,7 +17,7 @@ def tui_env(which: Annotated[Literal["PATH", "p", "ENV", "e"], typer.Argument(he
18
17
  uv_with = ["textual"]
19
18
  uv_project_dir = None
20
19
  if not Path.home().joinpath("code/machineconfig").exists():
21
- uv_with.append("machineconfig>=8.14")
20
+ uv_with.append("machineconfig>=8.45")
22
21
  else:
23
22
  uv_project_dir = str(Path.home().joinpath("code/machineconfig"))
24
23
  run_shell_script(
@@ -37,7 +36,7 @@ def init_project(
37
36
  ] = None,
38
37
  group: Annotated[
39
38
  Optional[str], typer.Option("--group", "-g", help="group of packages names (no separation) p:plot, t:types, l:linting, i:interactive, d:data")
40
- ] = "ptlid",
39
+ ] = "p,t,l,i,d",
41
40
  ) -> None:
42
41
  if libraries is not None:
43
42
  packages_add_line = f"uv add {libraries}"
@@ -51,7 +50,9 @@ def init_project(
51
50
  typer.echo(f"❌ Error: pyproject.toml not found in {repo_root}", err=True)
52
51
  raise typer.Exit(code=1)
53
52
  starting_code = ""
53
+ agents_line = ""
54
54
  else:
55
+ agents_line = """agents make-config"""
55
56
  if name is not None:
56
57
  from machineconfig.utils.accessories import randstr
57
58
 
@@ -68,36 +69,49 @@ uv init --python {python}
68
69
  uv venv
69
70
  """
70
71
  print(f"Adding group `{group}` with common data science and plotting packages...")
71
- total_packages: list[str] = []
72
+ total_packages: dict[str, list[str]] = {}
72
73
  if group is not None:
73
- if "t" in group:
74
- total_packages.append(
75
- "types-python-dateutil types-pyyaml types-requests types-tqdm types-mysqlclient types-paramiko types-pytz types-sqlalchemy types-toml types-urllib3"
76
- )
77
- if "l" in group:
78
- total_packages.append("mypy pyright ruff pylint pyrefly cleanpy ipdb pudb")
79
- if "i" in group:
80
- total_packages.append("ipython ipykernel jupyterlab nbformat marimo")
81
- if "p" in group:
82
- total_packages.append("python-magic matplotlib plotly kaleido")
83
- if "d" in group:
84
- total_packages.append("numpy pandas polars duckdb-engine sqlalchemy psycopg2-binary pyarrow tqdm openpyxl")
74
+ packages = group.split(",")
75
+ if "t" in packages or "types" in packages:
76
+ total_packages["types"] = [
77
+ "types-python-dateutil",
78
+ "types-pyyaml",
79
+ "types-requests",
80
+ "types-tqdm",
81
+ "types-mysqlclient",
82
+ "types-paramiko",
83
+ "types-pytz",
84
+ "types-sqlalchemy",
85
+ "types-toml",
86
+ "types-urllib3",
87
+ ]
88
+ if "l" in packages:
89
+ total_packages["linting"] = ["mypy", "pyright", "ruff", "pylint", "pyrefly", "cleanpy", "ipdb", "pudb"]
90
+ if "i" in packages:
91
+ total_packages["interactive"] = ["ipython", "ipykernel", "jupyterlab", "nbformat", "marimo"]
92
+ if "p" in packages:
93
+ total_packages["plot"] = ["python-magic", "matplotlib", "plotly", "kaleido"]
94
+ if "d" in packages:
95
+ total_packages["data"] = ["numpy", "pandas", "polars", "duckdb-engine", "sqlalchemy", "psycopg2-binary", "pyarrow", "tqdm", "openpyxl"]
85
96
  from machineconfig.utils.ve import get_ve_activate_line
86
-
97
+ groups_packages_lines = "\n".join(
98
+ [f"uv add --group {group_name} {' '.join(packages)}" for group_name, packages in total_packages.items()]
99
+ )
87
100
  script = f"""
88
101
  {starting_code}
89
102
  {packages_add_line}
90
- uv add --group {group} {" ".join(total_packages)}
103
+ {groups_packages_lines}
91
104
  {get_ve_activate_line(ve_root=str(repo_root.joinpath(".venv")))}
105
+ {agents_line}
92
106
  ls
93
107
  """
94
108
  from machineconfig.utils.code import exit_then_run_shell_script, run_shell_script
95
- # exit_then_run_shell_script(script)
96
- _ = exit_then_run_shell_script
97
- run_shell_script(script)
98
- if tempdir:
99
- from machineconfig.scripts.python.ai.initai import add_ai_configs
100
- add_ai_configs(repo_root=repo_root)
109
+ exit_then_run_shell_script(script)
110
+ _ = exit_then_run_shell_script, run_shell_script
111
+ # run_shell_script(script)
112
+ # if tempdir:
113
+ # from machineconfig.scripts.python.ai.initai import add_ai_configs
114
+ # add_ai_configs(repo_root=repo_root)
101
115
 
102
116
 
103
117
  def edit_file_with_hx(
@@ -144,8 +158,14 @@ class MachineSpecs(TypedDict):
144
158
  user: str
145
159
 
146
160
 
147
- def get_machine_specs() -> MachineSpecs:
161
+ def get_machine_specs(hardware: Annotated[bool, typer.Option(..., "--hardware", "-h", help="Show compute capability")] = False) -> MachineSpecs:
148
162
  """Write print and return the local machine specs."""
163
+ if hardware:
164
+ from machineconfig.scripts.python.helpers.helpers_utils.specs import main
165
+ main()
166
+ import sys
167
+ sys.exit()
168
+
149
169
  import platform
150
170
  from machineconfig.utils.code import get_uv_command
151
171
 
@@ -0,0 +1,246 @@
1
+ """
2
+ CPU Specification Helper.
3
+
4
+ This module provides utilities to detect the CPU model and fetch Geekbench scores
5
+ using the geekbench-browser-python tool.
6
+ """
7
+
8
+ import platform
9
+ import re
10
+ import subprocess
11
+
12
+
13
+ def get_cpu_name() -> str:
14
+ """
15
+ Detect the CPU model name across different operating systems.
16
+
17
+ Returns:
18
+ str: The detected CPU model name or 'Unknown CPU'.
19
+ """
20
+ system = platform.system()
21
+
22
+ if system == "Linux":
23
+ try:
24
+ with open("/proc/cpuinfo") as f:
25
+ for line in f:
26
+ if "model name" in line:
27
+ return line.split(":", 1)[1].strip()
28
+ except FileNotFoundError:
29
+ pass
30
+
31
+ elif system == "Windows":
32
+ try:
33
+ # shell=True might be needed for wmic depending on environment,
34
+ # but usually check_output with string list is safer if executable is in path.
35
+ # User provided: wmic cpu get name
36
+ output = subprocess.check_output("wmic cpu get name", shell=True).decode()
37
+ lines = output.strip().split("\n")
38
+ if len(lines) > 1:
39
+ return lines[1].strip()
40
+ except (subprocess.CalledProcessError, IndexError):
41
+ pass
42
+
43
+ elif system == "Darwin":
44
+ try:
45
+ return subprocess.check_output(
46
+ ["sysctl", "-n", "machdep.cpu.brand_string"]
47
+ ).decode().strip()
48
+ except subprocess.CalledProcessError:
49
+ pass
50
+
51
+ return "Unknown CPU"
52
+
53
+
54
+ def clean_cpu_name(cpu_name: str) -> str:
55
+ """
56
+ Clean the CPU name to improve Geekbench search results.
57
+
58
+ Removes extra details like 'w/ Radeon Graphics', frequency, etc.
59
+
60
+ Args:
61
+ cpu_name (str): The raw CPU name string.
62
+
63
+ Returns:
64
+ str: A cleaned CPU name suitable for searching.
65
+ """
66
+ # Remove "w/ ..." or "with ..."
67
+ # Example: AMD Ryzen 7 8745HS w/ Radeon 780M Graphics -> AMD Ryzen 7 8745HS
68
+ cleaned = re.split(r"\s+(w/|with)\s+", cpu_name, flags=re.IGNORECASE)[0]
69
+
70
+ # Remove (R) and (TM) symbols which confuse regex search and are often noisy
71
+ cleaned = re.sub(r"\([RT]M?\)", "", cleaned, flags=re.IGNORECASE)
72
+
73
+ # Remove clock speed like " @ 3.00GHz", " 3.00GHz", " 3.2GHz"
74
+ cleaned = re.sub(r"\s+@?\s*\d+(\.\d+)?\s*GHz", "", cleaned, flags=re.IGNORECASE)
75
+
76
+ # Remove "12-Core Processor" etc if present (some linux distros add this)
77
+ cleaned = re.sub(r"\s+\d+-Core\s+Processor", "", cleaned, flags=re.IGNORECASE)
78
+
79
+ # Remove trailing "Processor" word if it's there
80
+ cleaned = re.sub(r"\s+Processor", "", cleaned, flags=re.IGNORECASE)
81
+
82
+ # Collapse multiple spaces
83
+ cleaned = re.sub(r"\s+", " ", cleaned)
84
+
85
+ return cleaned.strip()
86
+
87
+
88
+ def run_geekbench_lookup(search_term: str) -> bool:
89
+ """
90
+ Run the geekbench search using uvx.
91
+
92
+ Args:
93
+ search_term (str): The CPU name to search for (supports Regex).
94
+
95
+ Returns:
96
+ bool: True if results were found, False otherwise.
97
+ """
98
+ # Note: uvx caches the tool so subsequent runs are fast.
99
+ # The 'gbr' tool uses pandas.str.contains(regex=True), so we can use regex patterns.
100
+ # We quote the search term to ensure it's treated as a single pattern containing spaces.
101
+ cmd = [
102
+ "uvx",
103
+ "--from",
104
+ "geekbench-browser-python",
105
+ "gbr",
106
+ search_term,
107
+ "--verbose",
108
+ ]
109
+ printable_cmd = " ".join(f'"{x}"' if " " in x else x for x in cmd)
110
+ print(f"Running: {printable_cmd}")
111
+ try:
112
+ # Capture output to check for results
113
+ # We enforce utf-8 encoding because 'geekbench-browser-python' likely outputs
114
+ # Unicode characters (Rich tables, box drawing) which can fail to decode or
115
+ # appear as garbage on Windows (default cp1252) if not handled.
116
+ result = subprocess.run(
117
+ cmd,
118
+ check=True,
119
+ stdout=subprocess.PIPE,
120
+ stderr=subprocess.PIPE,
121
+ text=True,
122
+ encoding="utf-8",
123
+ errors="replace",
124
+ )
125
+ output = result.stdout
126
+
127
+ # Check if we have data rows.
128
+ # Check iteratively for lines that look like data rows (Unicode or ASCII).
129
+ lines = output.splitlines()
130
+ has_data = False
131
+ for line in lines:
132
+ line_stripped = line.strip()
133
+ # Unicode table data row start (Light Vertical)
134
+ if "│" in line:
135
+ has_data = True
136
+ break
137
+ # ASCII table data row start (Pipe)
138
+ # Must exclude headers and separators which also start with pipe in ASCII mode.
139
+ if line_stripped.startswith("|"):
140
+ lower_line = line.lower()
141
+ # Skip header/separator lines
142
+ if "description" in lower_line or "single" in lower_line or "---" in line:
143
+ continue
144
+ has_data = True
145
+ break
146
+
147
+ if has_data:
148
+ print(output)
149
+ return True
150
+ else:
151
+ # If verbose debugging is needed, we could print output here.
152
+ # But normally if no table is found, the tool might have just printed log info to stderr.
153
+ # If it did print something to stdout that isn't a table, we probably shouldn't show it
154
+ # as a "result", but it might be helpful for debugging why it failed.
155
+ if output.strip():
156
+ # Only print debug info if it's NOT just an empty table structure to avoid noise
157
+ if not ("description" in output.lower() and ("---" in output or "│" in output)):
158
+ print(f"Debug output from tool:\n{output}")
159
+
160
+ print(f"No results found for '{search_term}'.")
161
+ return False
162
+
163
+ except subprocess.CalledProcessError:
164
+ print("Failed to retrieve Geekbench scores (command failed).")
165
+ return False
166
+ except FileNotFoundError:
167
+ print("Error: 'uvx' command not found. Please ensure 'uv' is installed.")
168
+ return False
169
+
170
+
171
+ def main() -> None:
172
+ """Main entry point."""
173
+ cpu_name = get_cpu_name()
174
+ print(f"Detected CPU: {cpu_name}")
175
+
176
+ if cpu_name == "Unknown CPU":
177
+ print("Could not detect CPU name. Exiting.")
178
+ return
179
+
180
+ full_search_term = clean_cpu_name(cpu_name)
181
+
182
+ # Retry logic: remove last word until we find something or run out of words
183
+ # We escape each word to ensure regex special characters (like +, (), etc) in the name
184
+ # are treated as literals, while preserving our ability to use regex wildcards later.
185
+ words = [re.escape(w) for w in full_search_term.split()]
186
+
187
+ while words:
188
+ current_term = " ".join(words)
189
+
190
+ # Heuristic: Don't search for extremely short generic terms if possible,
191
+ # but "AMD" or "Intel" might be what we end up with if nothing else works.
192
+ # Let's try at least length 2 words unless it's just 1 word left.
193
+ if len(words) > 1 and len(current_term) < 4:
194
+ words.pop()
195
+ continue
196
+
197
+ print(f"Search Term: {current_term}")
198
+ if run_geekbench_lookup(current_term):
199
+ return
200
+
201
+ last_word = words[-1]
202
+ numeric_part = None
203
+
204
+ # Check if the last word starts with digits (e.g. 8745HS or 8745)
205
+ # We want to both strip suffix AND try wildcards.
206
+ match_digits = re.match(r"^(\d+)([a-zA-Z].*)?$", last_word)
207
+
208
+ if match_digits:
209
+ numeric_part = match_digits.group(1)
210
+ suffix_part = match_digits.group(2) # May be None
211
+
212
+ # Intermediate Try 1: Strip suffix (e.g. 8745HS -> 8745)
213
+ if suffix_part and len(numeric_part) >= 3:
214
+ intermediate_term = " ".join(words[:-1] + [numeric_part])
215
+ if intermediate_term != current_term:
216
+ print(f"Search Term: {intermediate_term} (stripped suffix)")
217
+ if run_geekbench_lookup(intermediate_term):
218
+ return
219
+
220
+ # Intermediate Try 2: Wildcard/Regex search (e.g. 8745 -> 87..)
221
+ # The tool uses Regex, so we use dots '.' to match any character instead of '?'
222
+ if numeric_part and len(numeric_part) >= 3:
223
+ # If 4+ digits (e.g. 8745), replace last 2 with .. -> 87..
224
+ # If 3 digits (e.g. 780), replace last 1 with . -> 78.
225
+ if len(numeric_part) >= 4:
226
+ wildcard_model = numeric_part[:-2] + ".."
227
+ else:
228
+ wildcard_model = numeric_part[:-1] + "."
229
+
230
+ wildcard_term = " ".join(words[:-1] + [wildcard_model])
231
+
232
+ # Check duplication against current_term and the stripped version
233
+ stripped_term = " ".join(words[:-1] + [numeric_part])
234
+ if wildcard_term != current_term and wildcard_term != stripped_term:
235
+ print(f"Search Term: {wildcard_term} (regex wildcard)")
236
+ if run_geekbench_lookup(wildcard_term):
237
+ return
238
+
239
+ # Remove last word for next iteration
240
+ words.pop()
241
+
242
+ print("Could not find any matching Geekbench scores.")
243
+
244
+
245
+ if __name__ == "__main__":
246
+ main()