machineconfig 1.97__py3-none-any.whl → 2.1__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 (268) hide show
  1. machineconfig/cluster/cloud_manager.py +22 -29
  2. machineconfig/cluster/data_transfer.py +2 -3
  3. machineconfig/cluster/distribute.py +0 -2
  4. machineconfig/cluster/file_manager.py +4 -5
  5. machineconfig/cluster/job_params.py +1 -4
  6. machineconfig/cluster/loader_runner.py +8 -11
  7. machineconfig/cluster/remote_machine.py +4 -5
  8. machineconfig/cluster/script_execution.py +2 -2
  9. machineconfig/cluster/script_notify_upon_completion.py +0 -1
  10. machineconfig/cluster/sessions_managers/archive/create_zellij_template.py +4 -6
  11. machineconfig/cluster/sessions_managers/archive/session_managers.py +0 -1
  12. machineconfig/cluster/sessions_managers/enhanced_command_runner.py +35 -75
  13. machineconfig/cluster/sessions_managers/wt_local.py +113 -185
  14. machineconfig/cluster/sessions_managers/wt_local_manager.py +127 -197
  15. machineconfig/cluster/sessions_managers/wt_remote.py +60 -67
  16. machineconfig/cluster/sessions_managers/wt_remote_manager.py +110 -149
  17. machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +61 -64
  18. machineconfig/cluster/sessions_managers/wt_utils/process_monitor.py +72 -172
  19. machineconfig/cluster/sessions_managers/wt_utils/remote_executor.py +27 -60
  20. machineconfig/cluster/sessions_managers/wt_utils/session_manager.py +58 -137
  21. machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +46 -74
  22. machineconfig/cluster/sessions_managers/zellij_local.py +91 -147
  23. machineconfig/cluster/sessions_managers/zellij_local_manager.py +165 -190
  24. machineconfig/cluster/sessions_managers/zellij_remote.py +51 -58
  25. machineconfig/cluster/sessions_managers/zellij_remote_manager.py +40 -46
  26. machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +19 -17
  27. machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +30 -31
  28. machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +64 -134
  29. machineconfig/cluster/sessions_managers/zellij_utils/remote_executor.py +7 -11
  30. machineconfig/cluster/sessions_managers/zellij_utils/session_manager.py +27 -55
  31. machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +14 -13
  32. machineconfig/cluster/templates/cli_click.py +0 -1
  33. machineconfig/cluster/templates/cli_gooey.py +0 -2
  34. machineconfig/cluster/templates/cli_trogon.py +0 -1
  35. machineconfig/cluster/templates/run_cloud.py +0 -1
  36. machineconfig/cluster/templates/run_cluster.py +0 -1
  37. machineconfig/cluster/templates/run_remote.py +0 -1
  38. machineconfig/cluster/templates/utils.py +27 -11
  39. machineconfig/jobs/__pycache__/__init__.cpython-313.pyc +0 -0
  40. machineconfig/jobs/linux/msc/cli_agents.sh +16 -0
  41. machineconfig/jobs/python/check_installations.py +9 -9
  42. machineconfig/jobs/python/create_bootable_media.py +0 -2
  43. machineconfig/jobs/python/python_cargo_build_share.py +2 -2
  44. machineconfig/jobs/python/python_ve_symlink.py +9 -11
  45. machineconfig/jobs/python/tasks.py +0 -1
  46. machineconfig/jobs/python/vscode/api.py +5 -5
  47. machineconfig/jobs/python/vscode/link_ve.py +20 -21
  48. machineconfig/jobs/python/vscode/select_interpreter.py +28 -29
  49. machineconfig/jobs/python/vscode/sync_code.py +14 -18
  50. machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  51. machineconfig/jobs/python_custom_installers/archive/ngrok.py +15 -15
  52. machineconfig/jobs/python_custom_installers/dev/aider.py +10 -18
  53. machineconfig/jobs/python_custom_installers/dev/alacritty.py +12 -21
  54. machineconfig/jobs/python_custom_installers/dev/brave.py +13 -22
  55. machineconfig/jobs/python_custom_installers/dev/bypass_paywall.py +13 -20
  56. machineconfig/jobs/python_custom_installers/dev/code.py +17 -24
  57. machineconfig/jobs/python_custom_installers/dev/cursor.py +10 -21
  58. machineconfig/jobs/python_custom_installers/dev/docker_desktop.py +12 -11
  59. machineconfig/jobs/python_custom_installers/dev/espanso.py +19 -23
  60. machineconfig/jobs/python_custom_installers/dev/goes.py +9 -16
  61. machineconfig/jobs/python_custom_installers/dev/lvim.py +13 -21
  62. machineconfig/jobs/python_custom_installers/dev/nerdfont.py +15 -22
  63. machineconfig/jobs/python_custom_installers/dev/redis.py +15 -23
  64. machineconfig/jobs/python_custom_installers/dev/wezterm.py +15 -22
  65. machineconfig/jobs/python_custom_installers/dev/winget.py +32 -50
  66. machineconfig/jobs/python_custom_installers/docker.py +15 -24
  67. machineconfig/jobs/python_custom_installers/gh.py +18 -26
  68. machineconfig/jobs/python_custom_installers/hx.py +33 -17
  69. machineconfig/jobs/python_custom_installers/warp-cli.py +15 -23
  70. machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  71. machineconfig/jobs/python_generic_installers/config.json +412 -389
  72. machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  73. machineconfig/jobs/python_windows_installers/dev/config.json +1 -1
  74. machineconfig/jobs/windows/archive/archive_pygraphviz.ps1 +1 -1
  75. machineconfig/jobs/windows/msc/cli_agents.bat +0 -0
  76. machineconfig/jobs/windows/msc/cli_agents.ps1 +0 -0
  77. machineconfig/jobs/windows/start_terminal.ps1 +1 -1
  78. machineconfig/logger.py +50 -0
  79. machineconfig/profile/create.py +50 -36
  80. machineconfig/profile/create_hardlinks.py +33 -26
  81. machineconfig/profile/shell.py +87 -60
  82. machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
  83. machineconfig/scripts/cloud/init.sh +2 -2
  84. machineconfig/scripts/linux/checkout_versions +1 -1
  85. machineconfig/scripts/linux/choose_wezterm_theme +1 -1
  86. machineconfig/scripts/linux/cloud_copy +1 -1
  87. machineconfig/scripts/linux/cloud_manager +1 -1
  88. machineconfig/scripts/linux/cloud_mount +1 -1
  89. machineconfig/scripts/linux/cloud_repo_sync +1 -1
  90. machineconfig/scripts/linux/cloud_sync +1 -1
  91. machineconfig/scripts/linux/croshell +1 -1
  92. machineconfig/scripts/linux/devops +3 -5
  93. machineconfig/scripts/linux/fire +2 -1
  94. machineconfig/scripts/linux/fire_agents +3 -3
  95. machineconfig/scripts/linux/ftpx +1 -1
  96. machineconfig/scripts/linux/gh_models +1 -1
  97. machineconfig/scripts/linux/kill_process +1 -1
  98. machineconfig/scripts/linux/mcinit +2 -2
  99. machineconfig/scripts/linux/repos +1 -1
  100. machineconfig/scripts/linux/scheduler +1 -1
  101. machineconfig/scripts/linux/start_slidev +1 -1
  102. machineconfig/scripts/linux/start_terminals +1 -1
  103. machineconfig/scripts/linux/url2md +1 -1
  104. machineconfig/scripts/linux/warp-cli.sh +122 -0
  105. machineconfig/scripts/linux/wifi_conn +1 -1
  106. machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc +0 -0
  107. machineconfig/scripts/python/__pycache__/croshell.cpython-313.pyc +0 -0
  108. machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc +0 -0
  109. machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-313.pyc +0 -0
  110. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc +0 -0
  111. machineconfig/scripts/python/__pycache__/fire_jobs.cpython-313.pyc +0 -0
  112. machineconfig/scripts/python/ai/__init__.py +0 -0
  113. machineconfig/scripts/python/ai/__pycache__/__init__.cpython-313.pyc +0 -0
  114. machineconfig/scripts/python/ai/__pycache__/generate_files.cpython-313.pyc +0 -0
  115. machineconfig/scripts/python/ai/__pycache__/mcinit.cpython-313.pyc +0 -0
  116. machineconfig/scripts/python/ai/chatmodes/Thinking-Beast-Mode.chatmode.md +337 -0
  117. machineconfig/scripts/python/ai/chatmodes/Ultimate-Transparent-Thinking-Beast-Mode.chatmode.md +644 -0
  118. machineconfig/scripts/python/ai/chatmodes/deepResearch.chatmode.md +81 -0
  119. machineconfig/scripts/python/ai/configs/.gemini/settings.json +81 -0
  120. machineconfig/scripts/python/ai/generate_files.py +84 -0
  121. machineconfig/scripts/python/ai/instructions/python/dev.instructions.md +45 -0
  122. machineconfig/scripts/python/ai/mcinit.py +107 -0
  123. machineconfig/scripts/python/ai/prompts/allLintersAndTypeCheckers.prompt.md +5 -0
  124. machineconfig/scripts/python/ai/prompts/research-report-skeleton.prompt.md +38 -0
  125. machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +52 -0
  126. machineconfig/scripts/python/archive/tmate_conn.py +5 -5
  127. machineconfig/scripts/python/archive/tmate_start.py +3 -3
  128. machineconfig/scripts/python/choose_wezterm_theme.py +2 -2
  129. machineconfig/scripts/python/cloud_copy.py +20 -19
  130. machineconfig/scripts/python/cloud_mount.py +10 -8
  131. machineconfig/scripts/python/cloud_repo_sync.py +15 -15
  132. machineconfig/scripts/python/cloud_sync.py +1 -1
  133. machineconfig/scripts/python/croshell.py +18 -16
  134. machineconfig/scripts/python/devops.py +6 -6
  135. machineconfig/scripts/python/devops_add_identity.py +9 -7
  136. machineconfig/scripts/python/devops_add_ssh_key.py +19 -19
  137. machineconfig/scripts/python/devops_backup_retrieve.py +14 -14
  138. machineconfig/scripts/python/devops_devapps_install.py +3 -3
  139. machineconfig/scripts/python/devops_update_repos.py +141 -53
  140. machineconfig/scripts/python/dotfile.py +3 -3
  141. machineconfig/scripts/python/fire_agents.py +202 -41
  142. machineconfig/scripts/python/fire_jobs.py +20 -21
  143. machineconfig/scripts/python/ftpx.py +4 -3
  144. machineconfig/scripts/python/gh_models.py +94 -94
  145. machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-313.pyc +0 -0
  146. machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-313.pyc +0 -0
  147. machineconfig/scripts/python/helpers/cloud_helpers.py +3 -3
  148. machineconfig/scripts/python/helpers/helpers2.py +3 -3
  149. machineconfig/scripts/python/helpers/helpers4.py +8 -7
  150. machineconfig/scripts/python/helpers/helpers5.py +7 -7
  151. machineconfig/scripts/python/helpers/repo_sync_helpers.py +2 -2
  152. machineconfig/scripts/python/mount_nfs.py +4 -3
  153. machineconfig/scripts/python/mount_nw_drive.py +4 -4
  154. machineconfig/scripts/python/mount_ssh.py +4 -3
  155. machineconfig/scripts/python/repos.py +9 -9
  156. machineconfig/scripts/python/scheduler.py +1 -1
  157. machineconfig/scripts/python/start_slidev.py +9 -8
  158. machineconfig/scripts/python/start_terminals.py +1 -1
  159. machineconfig/scripts/python/viewer.py +40 -40
  160. machineconfig/scripts/python/wifi_conn.py +65 -66
  161. machineconfig/scripts/python/wsl_windows_transfer.py +2 -2
  162. machineconfig/scripts/windows/checkout_version.ps1 +1 -3
  163. machineconfig/scripts/windows/choose_wezterm_theme.ps1 +1 -3
  164. machineconfig/scripts/windows/cloud_copy.ps1 +2 -6
  165. machineconfig/scripts/windows/cloud_manager.ps1 +1 -1
  166. machineconfig/scripts/windows/cloud_repo_sync.ps1 +1 -2
  167. machineconfig/scripts/windows/cloud_sync.ps1 +2 -2
  168. machineconfig/scripts/windows/croshell.ps1 +2 -2
  169. machineconfig/scripts/windows/devops.ps1 +1 -4
  170. machineconfig/scripts/windows/dotfile.ps1 +1 -3
  171. machineconfig/scripts/windows/fire.ps1 +1 -1
  172. machineconfig/scripts/windows/ftpx.ps1 +2 -2
  173. machineconfig/scripts/windows/gpt.ps1 +1 -1
  174. machineconfig/scripts/windows/kill_process.ps1 +1 -2
  175. machineconfig/scripts/windows/mcinit.ps1 +2 -2
  176. machineconfig/scripts/windows/mount_nfs.ps1 +1 -1
  177. machineconfig/scripts/windows/mount_ssh.ps1 +1 -1
  178. machineconfig/scripts/windows/pomodoro.ps1 +1 -1
  179. machineconfig/scripts/windows/py2exe.ps1 +1 -3
  180. machineconfig/scripts/windows/repos.ps1 +1 -1
  181. machineconfig/scripts/windows/scheduler.ps1 +1 -1
  182. machineconfig/scripts/windows/snapshot.ps1 +2 -2
  183. machineconfig/scripts/windows/start_slidev.ps1 +1 -1
  184. machineconfig/scripts/windows/start_terminals.ps1 +1 -1
  185. machineconfig/scripts/windows/wifi_conn.ps1 +1 -1
  186. machineconfig/scripts/windows/wsl_windows_transfer.ps1 +1 -3
  187. machineconfig/settings/lf/linux/lfrc +1 -1
  188. machineconfig/settings/linters/.ruff.toml +2 -2
  189. machineconfig/settings/linters/.ruff_cache/.gitignore +2 -0
  190. machineconfig/settings/linters/.ruff_cache/CACHEDIR.TAG +1 -0
  191. machineconfig/settings/lvim/windows/archive/config_additional.lua +1 -1
  192. machineconfig/settings/shells/ipy/profiles/default/startup/playext.py +71 -71
  193. machineconfig/settings/shells/wt/settings.json +8 -8
  194. machineconfig/settings/svim/linux/init.toml +1 -1
  195. machineconfig/settings/svim/windows/init.toml +1 -1
  196. machineconfig/setup_linux/web_shortcuts/croshell.sh +0 -54
  197. machineconfig/setup_linux/web_shortcuts/interactive.sh +6 -6
  198. machineconfig/setup_linux/web_shortcuts/tmp.sh +2 -0
  199. machineconfig/setup_windows/web_shortcuts/all.ps1 +2 -2
  200. machineconfig/setup_windows/web_shortcuts/ascii_art.ps1 +1 -1
  201. machineconfig/setup_windows/web_shortcuts/croshell.ps1 +1 -1
  202. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +5 -5
  203. machineconfig/setup_windows/wt_and_pwsh/install_fonts.ps1 +51 -15
  204. machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +75 -18
  205. machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +52 -42
  206. machineconfig/utils/ai/browser_user_wrapper.py +5 -5
  207. machineconfig/utils/ai/generate_file_checklist.py +19 -22
  208. machineconfig/utils/ai/url2md.py +5 -3
  209. machineconfig/utils/cloud/onedrive/setup_oauth.py +5 -4
  210. machineconfig/utils/cloud/onedrive/transaction.py +192 -227
  211. machineconfig/utils/code.py +71 -43
  212. machineconfig/utils/installer.py +77 -85
  213. machineconfig/utils/installer_utils/installer_abc.py +29 -17
  214. machineconfig/utils/installer_utils/installer_class.py +188 -83
  215. machineconfig/utils/io_save.py +3 -15
  216. machineconfig/utils/links.py +22 -11
  217. machineconfig/utils/notifications.py +197 -0
  218. machineconfig/utils/options.py +38 -25
  219. machineconfig/utils/path.py +18 -6
  220. machineconfig/utils/path_reduced.py +637 -316
  221. machineconfig/utils/procs.py +69 -63
  222. machineconfig/utils/scheduling.py +11 -13
  223. machineconfig/utils/ssh.py +351 -0
  224. machineconfig/utils/terminal.py +225 -0
  225. machineconfig/utils/utils.py +13 -12
  226. machineconfig/utils/utils2.py +43 -10
  227. machineconfig/utils/utils5.py +242 -46
  228. machineconfig/utils/ve.py +11 -6
  229. {machineconfig-1.97.dist-info → machineconfig-2.1.dist-info}/METADATA +15 -9
  230. {machineconfig-1.97.dist-info → machineconfig-2.1.dist-info}/RECORD +232 -235
  231. machineconfig/cluster/self_ssh.py +0 -57
  232. machineconfig/jobs/__pycache__/__init__.cpython-311.pyc +0 -0
  233. machineconfig/jobs/python/__pycache__/__init__.cpython-311.pyc +0 -0
  234. machineconfig/jobs/python/archive/python_tools.txt +0 -12
  235. machineconfig/jobs/python/vscode/__pycache__/select_interpreter.cpython-311.pyc +0 -0
  236. machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  237. machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  238. machineconfig/jobs/python_generic_installers/update.py +0 -3
  239. machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  240. machineconfig/profile/__pycache__/__init__.cpython-311.pyc +0 -0
  241. machineconfig/profile/__pycache__/create.cpython-311.pyc +0 -0
  242. machineconfig/profile/__pycache__/shell.cpython-311.pyc +0 -0
  243. machineconfig/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  244. machineconfig/scripts/linux/activate_ve +0 -87
  245. machineconfig/scripts/python/__pycache__/__init__.cpython-311.pyc +0 -0
  246. machineconfig/scripts/python/__pycache__/cloud_copy.cpython-311.pyc +0 -0
  247. machineconfig/scripts/python/__pycache__/cloud_mount.cpython-311.pyc +0 -0
  248. machineconfig/scripts/python/__pycache__/cloud_sync.cpython-311.pyc +0 -0
  249. machineconfig/scripts/python/__pycache__/croshell.cpython-311.pyc +0 -0
  250. machineconfig/scripts/python/__pycache__/devops.cpython-311.pyc +0 -0
  251. machineconfig/scripts/python/__pycache__/devops_backup_retrieve.cpython-311.pyc +0 -0
  252. machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-311.pyc +0 -0
  253. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-311.pyc +0 -0
  254. machineconfig/scripts/python/__pycache__/fire_agents.cpython-311.pyc +0 -0
  255. machineconfig/scripts/python/__pycache__/fire_jobs.cpython-311.pyc +0 -0
  256. machineconfig/scripts/python/__pycache__/get_zellij_cmd.cpython-311.pyc +0 -0
  257. machineconfig/scripts/python/__pycache__/repos.cpython-311.pyc +0 -0
  258. machineconfig/scripts/python/ai/__pycache__/init.cpython-311.pyc +0 -0
  259. machineconfig/scripts/python/ai/init.py +0 -56
  260. machineconfig/scripts/python/ai/rules/python/dev.md +0 -31
  261. machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-311.pyc +0 -0
  262. machineconfig/scripts/python/helpers/__pycache__/cloud_helpers.cpython-311.pyc +0 -0
  263. machineconfig/scripts/python/helpers/__pycache__/helpers2.cpython-311.pyc +0 -0
  264. machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-311.pyc +0 -0
  265. machineconfig/scripts/python/helpers/__pycache__/repo_sync_helpers.cpython-311.pyc +0 -0
  266. machineconfig/scripts/windows/activate_ve.ps1 +0 -54
  267. {machineconfig-1.97.dist-info → machineconfig-2.1.dist-info}/WHEEL +0 -0
  268. {machineconfig-1.97.dist-info → machineconfig-2.1.dist-info}/top_level.txt +0 -0
@@ -2,7 +2,7 @@
2
2
  """
3
3
 
4
4
  # import subprocess
5
- from machineconfig.utils.path_reduced import P as PathExtended
5
+ from machineconfig.utils.path_reduced import PathExtended as PathExtended
6
6
  from machineconfig.utils.utils2 import read_ini, read_toml
7
7
  from machineconfig.utils.utils import LIBRARY_ROOT, DEFAULTS_PATH, print_code, choose_cloud_interactively, choose_multiple_options
8
8
  from machineconfig.scripts.python.helpers.helpers2 import ES
@@ -17,7 +17,7 @@ OPTIONS = Literal["BACKUP", "RETRIEVE"]
17
17
 
18
18
  def main_backup_retrieve(direction: OPTIONS, which: Optional[str] = None):
19
19
  console = Console()
20
-
20
+
21
21
  try:
22
22
  cloud: str = read_ini(DEFAULTS_PATH)['general']['rclone_config_name']
23
23
  console.print(Panel(f"⚠️ DEFAULT CLOUD CONFIGURATION\n🌥️ Using default cloud: {cloud}", title="[bold blue]Cloud Configuration[/bold blue]", border_style="blue"))
@@ -26,13 +26,13 @@ def main_backup_retrieve(direction: OPTIONS, which: Optional[str] = None):
26
26
  cloud = choose_cloud_interactively()
27
27
 
28
28
  bu_file: dict[str, Any] = read_toml(LIBRARY_ROOT.joinpath("profile/backup.toml"))
29
-
29
+
30
30
  console.print(Panel(f"🧰 LOADING BACKUP CONFIGURATION\n📄 File: {LIBRARY_ROOT.joinpath('profile/backup.toml')}", title="[bold blue]Backup Configuration[/bold blue]", border_style="blue"))
31
-
32
- if system() == "Linux":
31
+
32
+ if system() == "Linux":
33
33
  bu_file = {key: val for key, val in bu_file.items() if "windows" not in key}
34
34
  console.print(Panel(f"🐧 LINUX ENVIRONMENT DETECTED\n🔍 Filtering out Windows-specific entries\n✅ Found {len(bu_file)} applicable backup configuration entries", title="[bold blue]Linux Environment[/bold blue]", border_style="blue"))
35
- elif system() == "Windows":
35
+ elif system() == "Windows":
36
36
  bu_file = {key: val for key, val in bu_file.items() if "linux" not in key}
37
37
  console.print(Panel(f"🪟 WINDOWS ENVIRONMENT DETECTED\n🔍 Filtering out Linux-specific entries\n✅ Found {len(bu_file)} applicable backup configuration entries", title="[bold blue]Windows Environment[/bold blue]", border_style="blue"))
38
38
 
@@ -59,14 +59,14 @@ def main_backup_retrieve(direction: OPTIONS, which: Optional[str] = None):
59
59
  flags += 'o' if system().lower() in item_name else ''
60
60
  console.print(Panel(f"📦 PROCESSING: {item_name}\n📂 Path: {PathExtended(item['path']).as_posix()}\n🏳️ Flags: {flags or 'None'}", title=f"[bold blue]Processing Item: {item_name}[/bold blue]", border_style="blue"))
61
61
  if flags: flags = "-" + flags
62
- if direction == "BACKUP":
62
+ if direction == "BACKUP":
63
63
  program += f"""\ncloud_copy "{PathExtended(item['path']).as_posix()}" $cloud {flags}\n"""
64
- elif direction == "RETRIEVE":
64
+ elif direction == "RETRIEVE":
65
65
  program += f"""\ncloud_copy $cloud "{PathExtended(item['path']).as_posix()}" {flags}\n"""
66
66
  else:
67
67
  console.print(Panel("❌ ERROR: INVALID DIRECTION\n⚠️ Direction must be either \"BACKUP\" or \"RETRIEVE\"", title="[bold red]Error: Invalid Direction[/bold red]", border_style="red"))
68
- raise RuntimeError(f"Unknown direction: {direction}")
69
- if item_name == "dotfiles" and system() == "Linux":
68
+ raise RuntimeError(f"Unknown direction: {direction}")
69
+ if item_name == "dotfiles" and system() == "Linux":
70
70
  program += """\nchmod 700 ~/.ssh/*\n"""
71
71
  console.print(Panel("🔒 SPECIAL HANDLING: SSH PERMISSIONS\n🛠️ Setting secure permissions for SSH files\n📝 Command: chmod 700 ~/.ssh/*", title="[bold blue]Special Handling: SSH Permissions[/bold blue]", border_style="blue"))
72
72
  print_code(program, lexer="shell", desc=f"{direction} script")
@@ -76,14 +76,14 @@ def main_backup_retrieve(direction: OPTIONS, which: Optional[str] = None):
76
76
 
77
77
  def main(direction: OPTIONS, which: Optional[str] = None):
78
78
  console = Console()
79
-
79
+
80
80
  console.print(Panel(f"🔄 {direction} OPERATION STARTED\n⏱️ {'-' * 58}", title="[bold blue]Operation Initiated[/bold blue]", border_style="blue"))
81
-
81
+
82
82
  code = main_backup_retrieve(direction=direction, which=which)
83
83
  from machineconfig.utils.utils import write_shell_script_to_default_program_path
84
-
84
+
85
85
  console.print(Panel("💾 GENERATING SHELL SCRIPT\n📄 Filename: backup_retrieve.sh", title="[bold blue]Shell Script Generation[/bold blue]", border_style="blue"))
86
-
86
+
87
87
  write_shell_script_to_default_program_path(program=code, desc="backup_retrieve.sh", preserve_cwd=True, display=True, execute=False)
88
88
 
89
89
 
@@ -70,10 +70,10 @@ def get_programs_by_category(program_name: WHICH_CAT):
70
70
  program = ""
71
71
 
72
72
  case "SystemInstallers":
73
- if system() == "Windows": options_system = parse_apps_installer_windows(LIBRARY_ROOT.joinpath("setup_windows/apps.ps1").read_text())
73
+ if system() == "Windows": options_system = parse_apps_installer_windows(LIBRARY_ROOT.joinpath("setup_windows/apps.ps1").read_text(encoding="utf-8"))
74
74
  elif system() == "Linux":
75
- options_system_1 = parse_apps_installer_linux(LIBRARY_ROOT.joinpath("setup_linux/apps_dev.sh").read_text())
76
- options_system_2 = parse_apps_installer_linux(LIBRARY_ROOT.joinpath("setup_linux/apps.sh").read_text())
75
+ options_system_1 = parse_apps_installer_linux(LIBRARY_ROOT.joinpath("setup_linux/apps_dev.sh").read_text(encoding="utf-8"))
76
+ options_system_2 = parse_apps_installer_linux(LIBRARY_ROOT.joinpath("setup_linux/apps.sh").read_text(encoding="utf-8"))
77
77
  options_system = {**options_system_1, **options_system_2}
78
78
  else: raise NotImplementedError(f"❌ System {system()} not supported")
79
79
  program_names = choose_multiple_options(msg="", options=sorted(list(options_system.keys())), header="🚀 CHOOSE DEV APP")
@@ -1,21 +1,135 @@
1
1
  """Update repositories with fancy output
2
2
  """
3
-
4
- from machineconfig.utils.path_reduced import P as PathExtended
3
+ import git
4
+ import subprocess
5
+ import hashlib
6
+ from pathlib import Path
7
+ from machineconfig.utils.path_reduced import PathExtended as PathExtended
5
8
  from machineconfig.utils.utils import DEFAULTS_PATH
6
9
  from machineconfig.utils.utils2 import read_ini
7
- from platform import system
8
10
 
9
11
 
10
- sep = "\n"
12
+ def _get_file_hash(file_path: Path) -> str | None:
13
+ """Get SHA256 hash of a file, return None if file doesn't exist."""
14
+ if not file_path.exists():
15
+ return None
16
+ return hashlib.sha256(file_path.read_bytes()).hexdigest()
17
+
11
18
 
19
+ def _set_permissions_recursive(path: Path, executable: bool = True) -> None:
20
+ """Set permissions recursively for a directory."""
21
+ if not path.exists():
22
+ return
23
+
24
+ if path.is_file():
25
+ if executable:
26
+ path.chmod(0o755)
27
+ else:
28
+ path.chmod(0o644)
29
+ elif path.is_dir():
30
+ path.chmod(0o755)
31
+ for item in path.rglob('*'):
32
+ _set_permissions_recursive(item, executable)
12
33
 
13
- def main(verbose: bool=True) -> str:
34
+
35
+ def _run_uv_sync(repo_path: Path) -> None:
36
+ """Run uv sync in the given repository path."""
37
+ try:
38
+ print(f"🔄 Running uv sync in {repo_path}")
39
+ result = subprocess.run(
40
+ ["uv", "sync"],
41
+ cwd=repo_path,
42
+ capture_output=True,
43
+ text=True,
44
+ check=True
45
+ )
46
+ print("✅ uv sync completed successfully")
47
+ if result.stdout:
48
+ print(f"📝 Output: {result.stdout}")
49
+ except subprocess.CalledProcessError as e:
50
+ print(f"❌ uv sync failed: {e}")
51
+ if e.stderr:
52
+ print(f"📝 Error: {e.stderr}")
53
+ except FileNotFoundError:
54
+ print("⚠️ uv command not found. Please install uv first.")
55
+
56
+
57
+ def _update_repository(repo: git.Repo) -> bool:
58
+ """Update a single repository and return True if pyproject.toml or uv.lock changed."""
59
+ repo_path = Path(repo.working_dir)
60
+ print(f"🔄 {'Updating ' + str(repo_path):.^80}")
61
+
62
+ # Check if this repo has pyproject.toml or uv.lock
63
+ pyproject_path = repo_path / "pyproject.toml"
64
+ uv_lock_path = repo_path / "uv.lock"
65
+
66
+ # Get hashes before pull
67
+ pyproject_hash_before = _get_file_hash(pyproject_path)
68
+ uv_lock_hash_before = _get_file_hash(uv_lock_path)
69
+
70
+ try:
71
+ # Perform git pull for each remote
72
+ dependencies_changed = False
73
+ for remote in repo.remotes:
74
+ try:
75
+ print(f"📥 Pulling from {remote.name} {repo.active_branch.name}")
76
+ pull_info = remote.pull(repo.active_branch.name)
77
+ for info in pull_info:
78
+ if info.flags & info.FAST_FORWARD:
79
+ print("✅ Fast-forward pull completed")
80
+ elif info.flags & info.NEW_HEAD:
81
+ print("✅ Repository updated")
82
+ else:
83
+ print(f"✅ Pull completed: {info.flags}")
84
+ except Exception as e:
85
+ print(f"⚠️ Failed to pull from {remote.name}: {e}")
86
+ continue
87
+
88
+ # Check if pyproject.toml or uv.lock changed after pull
89
+ pyproject_hash_after = _get_file_hash(pyproject_path)
90
+ uv_lock_hash_after = _get_file_hash(uv_lock_path)
91
+
92
+ if pyproject_hash_before != pyproject_hash_after:
93
+ print("📋 pyproject.toml has changed")
94
+ dependencies_changed = True
95
+
96
+ if uv_lock_hash_before != uv_lock_hash_after:
97
+ print("🔒 uv.lock has changed")
98
+ dependencies_changed = True
99
+
100
+ # Special handling for machineconfig repository
101
+ if "machineconfig" in str(repo_path):
102
+ print("🛠 Special handling for machineconfig repository...")
103
+ scripts_path = Path.home() / "scripts"
104
+ if scripts_path.exists():
105
+ _set_permissions_recursive(scripts_path)
106
+ print(f"✅ Set permissions for {scripts_path}")
107
+
108
+ linux_jobs_path = repo_path / "src" / "machineconfig" / "jobs" / "linux"
109
+ if linux_jobs_path.exists():
110
+ _set_permissions_recursive(linux_jobs_path)
111
+ print(f"✅ Set permissions for {linux_jobs_path}")
112
+
113
+ lf_exe_path = repo_path / "src" / "machineconfig" / "settings" / "lf" / "linux" / "exe"
114
+ if lf_exe_path.exists():
115
+ _set_permissions_recursive(lf_exe_path)
116
+ print(f"✅ Set permissions for {lf_exe_path}")
117
+
118
+ return dependencies_changed
119
+
120
+ except Exception as e:
121
+ print(f"❌ Error updating repository {repo_path}: {e}")
122
+ return False
123
+
124
+
125
+ def main(verbose: bool = True) -> str:
126
+ """Main function to update all configured repositories."""
14
127
  _ = verbose
15
- repos: list[str] = ["~/code/crocodile", "~/code/machineconfig", ]
128
+ repos: list[str] = ["~/code/machineconfig", "~/code/machineconfig", ]
16
129
  try:
17
130
  tmp = read_ini(DEFAULTS_PATH)['general']['repos'].split(",")
18
- if tmp[-1] == "": tmp = tmp[:-1]
131
+ if tmp[-1] == "":
132
+ tmp = tmp[:-1]
19
133
  repos += tmp
20
134
  except (FileNotFoundError, KeyError, IndexError):
21
135
  print(f"""
@@ -26,7 +140,7 @@ def main(verbose: bool=True) -> str:
26
140
  print("""
27
141
  ┌────────────────────────────────────────────────────────────────
28
142
  │ ✨ Example Configuration:
29
-
143
+
30
144
  │ [general]
31
145
  │ repos = ~/code/repo1,~/code/repo2
32
146
  │ rclone_config_name = onedrivePersonal
@@ -34,57 +148,31 @@ def main(verbose: bool=True) -> str:
34
148
  │ to_email = myemail@email.com
35
149
  └────────────────────────────────────────────────────────────────""")
36
150
 
37
- repos_objs = []
151
+ # Process repositories
152
+ repos_with_changes = []
38
153
  for a_package_path in repos:
39
154
  try:
40
- import git
41
- repo = git.Repo(str(PathExtended(a_package_path).expanduser()), search_parent_directories=True)
42
- repos_objs.append(repo)
155
+ expanded_path = PathExtended(a_package_path).expanduser()
156
+ repo = git.Repo(str(expanded_path), search_parent_directories=True)
157
+
158
+ # Update repository and check if dependencies changed
159
+ dependencies_changed = _update_repository(repo)
160
+
161
+ if dependencies_changed:
162
+ repos_with_changes.append(Path(repo.working_dir))
163
+
43
164
  except Exception as ex:
44
- print(f"""
45
- Repository Error:
46
- Path: {a_package_path}
47
- Exception: {ex}
165
+ print(f"""❌ Repository Error: Path: {a_package_path}
166
+ Exception: {ex}
48
167
  {'-' * 50}""")
49
168
 
50
- if system() == "Linux":
51
- additions = []
52
- for a_repo in repos_objs:
53
- if "machineconfig" in str(a_repo.working_dir): # special treatment because of executables.
54
- an_addition = f"""
55
- echo ""
56
- echo "🔄 {("Updating " + str(a_repo.working_dir)).center(80, "═")}"
57
- echo "🛠 Special handling for machineconfig repository..."
58
- cd "{a_repo.working_dir}"
59
- # git reset --hard
60
- git pull origin &
61
- chmod +x ~/scripts -R
62
- chmod +x ~/code/machineconfig/src/machineconfig/jobs/linux -R
63
- chmod +x ~/code/machineconfig/src/machineconfig/settings/lf/linux/exe -R
64
- """
65
- additions.append(an_addition)
66
- else:
67
- additions.append(f"""
68
- echo "🔄 {("Updating " + str(a_repo.working_dir)).center(80, "═")}"
69
- cd "{a_repo.working_dir}"
70
- {sep.join([f'git pull {remote.name} {a_repo.active_branch.name} &' for remote in a_repo.remotes])}
71
- """
72
- )
73
- program = "\n".join(additions)
169
+ # Run uv sync for repositories where pyproject.toml or uv.lock changed
170
+ for repo_path in repos_with_changes:
171
+ _run_uv_sync(repo_path)
74
172
 
75
- elif system() == "Windows":
76
- program = "\n".join([f"""
77
- echo "🔄 {("Updating " + str(a_repo.working_dir)).center(80, "═")}"
78
- cd "{a_repo.working_dir}"
79
- {sep.join([f'git pull {remote.name} {a_repo.active_branch.name}' for remote in a_repo.remotes])}
80
- """ for a_repo in repos_objs])
81
- else: raise NotImplementedError(f"""
82
- ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
83
- ┃ ⚠️ Unsupported System: {system()}
84
- ┃ ℹ️ This functionality is only available on Windows and Linux
85
- ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━""")
86
- return program
173
+ # print("\n🎉 All repositories updated successfully!")
174
+ return """echo "🎉 All repositories updated successfully!" """
87
175
 
88
176
 
89
177
  if __name__ == '__main__':
90
- pass
178
+ main()
@@ -2,7 +2,7 @@
2
2
  """
3
3
 
4
4
 
5
- from machineconfig.utils.path_reduced import P as PathExtended
5
+ from machineconfig.utils.path_reduced import PathExtended as PathExtended
6
6
  from machineconfig.profile.create import symlink_func
7
7
  from machineconfig.utils.utils import LIBRARY_ROOT, REPO_ROOT
8
8
  import argparse
@@ -25,7 +25,7 @@ def main():
25
25
  elif ".config" in str(orig_path): junction = orig_path.split(at=".config", sep=-1)[1]
26
26
  else: junction = orig_path.rel2home()
27
27
  new_path = REPO_ROOT.joinpath(junction)
28
- else:
28
+ else:
29
29
  dest_path = PathExtended(args.dest).expanduser().absolute()
30
30
  dest_path.mkdir(parents=True, exist_ok=True)
31
31
  new_path = dest_path.joinpath(orig_path.name)
@@ -35,7 +35,7 @@ def main():
35
35
  print("""
36
36
  ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
37
37
  ┃ ✅ Symbolic Link Created Successfully
38
-
38
+
39
39
  ┃ 🔄 To enshrine this mapping, add the following to mapper.toml:
40
40
  ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━""")
41
41
  print(f"""
@@ -1,63 +1,224 @@
1
1
 
2
+ """Utility to launch multiple AI agent prompts in a Zellij session.
3
+
4
+ Improved design notes:
5
+ * Clear separation of: input collection, prompt preparation, agent launch.
6
+ * Configurable max agent cap (default 15) with interactive confirmation if exceeded.
7
+ * Added type aliases + docstrings for maintainability.
8
+ * Preserves original core behavior & command generation for each agent type.
9
+ """
10
+
11
+ from __future__ import annotations
2
12
 
3
13
  from pathlib import Path
4
- # from machineconfig.cluster.sessions_managers.zellij_local import run_zellij_layout
14
+ import shlex
15
+ from math import ceil
16
+ from typing import Literal, TypeAlias, get_args, Iterable
17
+
5
18
  from machineconfig.cluster.sessions_managers.zellij_local_manager import ZellijLocalManager
19
+ from machineconfig.utils.utils2 import randstr
20
+ import random
21
+ # import time
22
+
23
+ AGENTS: TypeAlias = Literal["cursor-agent", "gemini", "crush", "q",
24
+ # warp terminal
25
+ ]
26
+ TabConfig = dict[str, tuple[str, str]] # tab name -> (cwd, command)
27
+ DEFAULT_AGENT_CAP = 6
28
+
29
+
30
+ def get_gemini_api_keys() -> list[str]:
31
+ from machineconfig.utils.utils2 import read_ini
32
+ config = read_ini(Path.home().joinpath("dotfiles/creds/llm/gemini/api_keys.ini"))
33
+ res: list[str] = []
34
+ for a_section_name in list(config.sections()):
35
+ a_section = config[a_section_name]
36
+ if "api_key" in a_section:
37
+ api_key = a_section["api_key"].strip()
38
+ if api_key:
39
+ res.append(api_key)
40
+ # res = [v for k, v in config.items("api_keys") if k.startswith("key") and v.strip() != ""]
41
+ print(f"Found {len(res)} Gemini API keys configured.")
42
+ return res
43
+
44
+
45
+ def _search_python_files(repo_root: Path, keyword: str) -> list[Path]:
46
+ """Return all Python files under repo_root whose text contains keyword.
47
+
48
+ Errors reading individual files are ignored (decoded with 'ignore').
49
+ """
50
+ py_files = list(repo_root.rglob("*.py"))
51
+ keyword_lower = keyword.lower()
52
+ matches: list[Path] = []
53
+ for f in py_files:
54
+ try:
55
+ if keyword_lower in f.read_text(encoding="utf-8", errors="ignore").lower():
56
+ matches.append(f)
57
+ except OSError:
58
+ # Skip unreadable file
59
+ continue
60
+ return matches
61
+
62
+
63
+ def _write_list_file(target: Path, files: Iterable[Path]) -> None:
64
+ target.parent.mkdir(parents=True, exist_ok=True)
65
+ target.write_text("\n".join(str(f) for f in files), encoding="utf-8")
66
+ def _chunk_prompts(prompts: list[str], max_agents: int) -> list[str]:
67
+ prompts = [p for p in prompts if p.strip() != ""] # drop blank entries
68
+ if len(prompts) <= max_agents:
69
+ return prompts
70
+ print(f"Chunking {len(prompts)} prompts into groups for up to {max_agents} agents because it exceeds the cap.")
71
+ chunk_size = ceil(len(prompts) / max_agents)
72
+ grouped: list[str] = []
73
+ for i in range(0, len(prompts), chunk_size):
74
+ grouped.append("\nTargeted Locations:\n".join(prompts[i : i + chunk_size]))
75
+ return grouped
76
+ def _confirm(message: str, default_no: bool = True) -> bool:
77
+ suffix = "[y/N]" if default_no else "[Y/n]"
78
+ answer = input(f"{message} {suffix} ").strip().lower()
79
+ if answer in {"y", "yes"}:
80
+ return True
81
+ if not default_no and answer == "":
82
+ return True
83
+ return False
84
+
85
+
86
+ def launch_agents(repo_root: Path, prompts: list[str], agent: AGENTS, *, max_agents: int = DEFAULT_AGENT_CAP) -> TabConfig:
87
+ """Create tab configuration for a set of agent prompts.
6
88
 
89
+ If number of prompts exceeds max_agents, ask user for confirmation.
90
+ (Original behavior raised an error; now interactive override.)
91
+ """
92
+ if not prompts:
93
+ raise ValueError("No prompts provided")
94
+
95
+ if len(prompts) > max_agents:
96
+ proceed = _confirm(
97
+ message=(
98
+ f"You are about to launch {len(prompts)} agents which exceeds the cap ({max_agents}). Proceed?"
99
+ )
100
+ )
101
+ if not proceed:
102
+ print("Aborting per user choice.")
103
+ return {}
104
+
105
+ tab_config: TabConfig = {}
106
+ tmp_dir = repo_root / ".ai" / f"tmp_prompts/{randstr()}"
107
+ tmp_dir.mkdir(parents=True, exist_ok=True)
7
108
 
8
- def launch_agents(repo_root: Path, prompts: list[str]):
9
- tab_config: dict[str, tuple[str, str]] = {}
10
109
  for idx, a_prompt in enumerate(prompts):
11
- tmp_file_path = repo_root.joinpath(f".ai/tmp_prompts/agent{idx}.txt")
12
- tmp_file_path.parent.mkdir(parents=True, exist_ok=True)
13
- Path(tmp_file_path).write_text(a_prompt)
14
- tab_config[f"Agent{idx}"] = (str(repo_root), f"cursor-agent --print --output-format text < .ai/tmp_prompts/agent{idx}.txt")
15
- if len(tab_config) > 15:
16
- raise RuntimeError(f"Too many agents (#{len(tab_config)}), please cut them down to 15 or less")
110
+ prompt_path = tmp_dir / f"agent{idx}_prompt.txt"
111
+ prompt_path.write_text(a_prompt, encoding="utf-8")
112
+ cmd_path = tmp_dir / f"agent{idx}_cmd.sh"
113
+ match agent:
114
+ case "gemini":
115
+ # model = "gemini-2.5-pro"
116
+ # model = "gemini-2.5-flash-lite"
117
+ model = None # auto-select
118
+ if model is None:
119
+ model_arg = ""
120
+ else:
121
+ model_arg = f"--model {shlex.quote(model)}"
122
+ # Need a real shell for the pipeline; otherwise '| gemini ...' is passed as args to 'cat'
123
+ safe_path = shlex.quote(str(prompt_path))
124
+ api_keys = get_gemini_api_keys()
125
+ api_key = api_keys[idx % len(api_keys)] if api_keys else ""
126
+ # Export the environment variable so it's available to subshells
127
+ cmd = f"""
128
+ export GEMINI_API_KEY={shlex.quote(api_key)}
129
+ echo "Using Gemini API key $GEMINI_API_KEY"
130
+ cat {prompt_path}
131
+ GEMINI_API_KEY={shlex.quote(api_key)} bash -lc 'cat {safe_path} | gemini {model_arg} --yolo --prompt'
132
+ """
133
+ case "cursor-agent":
134
+ # As originally implemented
135
+ cmd = f"""
136
+
137
+ cursor-agent --print --output-format text < {prompt_path}
138
+
139
+ """
140
+ case "crush":
141
+ cmd = f"""
142
+ # cat {prompt_path} | crush run
143
+ crush run {prompt_path}
144
+ """
145
+ case "q":
146
+ cmd = f"""
147
+ q chat --no-interactive --trust-all-tools {prompt_path}
148
+ """
149
+ case _:
150
+ raise ValueError(f"Unsupported agent type: {agent}")
151
+ random_sleep_time = random.uniform(0, 5)
152
+ cmd_prefix = f"""
153
+ echo "Sleeping for {random_sleep_time:.2f} seconds to stagger agent startups..."
154
+ sleep {random_sleep_time:.2f}
155
+ echo "Launching `{agent}` with prompt from {prompt_path}"
156
+ echo "Launching `{agent}` with command from {cmd_path}"
157
+ echo "--------START OF AGENT OUTPUT--------"
158
+ sleep 0.1
159
+ """
160
+ cmd_postfix = """
161
+ sleep 0.1
162
+ echo "---------END OF AGENT OUTPUT---------"
163
+ """
164
+ cmd_path.write_text(cmd_prefix+cmd+cmd_postfix, encoding="utf-8")
165
+ fire_cmd = f"bash {shlex.quote(str(cmd_path))}"
166
+ tab_config[f"Agent{idx}"] = (str(repo_root), fire_cmd)
167
+
17
168
  print(f"Launching a template with #{len(tab_config)} agents")
18
169
  return tab_config
19
170
 
20
171
 
21
- def main():
172
+ def main(): # noqa: C901 - (complexity acceptable for CLI glue)
22
173
  repo_root = Path.cwd()
23
174
  print(f"Operating @ {repo_root}")
24
- file_path = input("Enter path to target file [press Enter to generate it from searching]: ")
25
- if file_path == "":
26
- keyword = input("Enter keyword to search recursively for all .py files containing it. ")
27
- py_files = list(repo_root.rglob("*.py"))
28
- matching_files = [
29
- f for f in py_files
30
- if keyword in f.read_text(encoding='utf-8', errors='ignore')
31
- ]
175
+
176
+ file_path_input = input("Enter path to target file [press Enter to generate it from searching]: ").strip()
177
+ if file_path_input == "":
178
+ keyword = input("Enter keyword to search recursively for all .py files containing it: ").strip()
179
+ if not keyword:
180
+ print("No keyword supplied. Exiting.")
181
+ return
182
+ matching_files = _search_python_files(repo_root, keyword)
32
183
  if not matching_files:
33
184
  print(f"💥 No .py files found containing keyword: {keyword}")
34
185
  return
35
- print(f"Found {len(matching_files)} .py files containing keyword: {keyword}")
36
- file_path = repo_root.joinpath(".ai", "target_file.txt")
37
- file_path.parent.mkdir(parents=True, exist_ok=True)
38
- file_path.write_text("\n".join(str(f) for f in matching_files), encoding='utf-8')
186
+ for idx, mf in enumerate(matching_files):
187
+ print(f"{idx:>3}: {mf}")
188
+ print(f"\nFound {len(matching_files)} .py files containing keyword: {keyword}")
189
+ target_list_file = repo_root / ".ai" / "target_file.txt"
190
+ _write_list_file(target_list_file, matching_files)
39
191
  separator = "\n"
192
+ source_text = target_list_file.read_text(encoding="utf-8", errors="ignore")
40
193
  else:
41
- separator = input("Enter separator [\\n]: ") or "\n"
194
+ target_file_path = Path(file_path_input).expanduser().resolve()
195
+ if not target_file_path.exists() or not target_file_path.is_file():
196
+ print(f"Invalid file path: {target_file_path}")
197
+ return
198
+ separator = input("Enter separator [\\n]: ").strip() or "\n"
199
+ if not target_file_path.exists():
200
+ print(f"File does not exist: {target_file_path}")
201
+ return
202
+ source_text = target_file_path.read_text(encoding="utf-8", errors="ignore")
42
203
 
43
- err_file = Path(file_path).expanduser().absolute().read_text(encoding='utf-8', errors='ignore')
204
+ raw_prompts = source_text.split(separator)
205
+ print(f"Loaded {len(raw_prompts)} raw prompts from source.")
44
206
  prefix = input("Enter prefix prompt: ")
45
- prompts = [item for item in err_file.split(separator)]
46
- # Dynamically choose chunk size so we end up with <= 15 combined prompts.
47
- # If we already have 15 or fewer, keep them as-is.
48
- if len(prompts) <= 15:
49
- combined_prompts = prompts
50
- else:
51
- # Compute minimal chunk size so that number_of_chunks <= 15.
52
- # number_of_chunks = ceil(len(prompts)/chunk_size) <= 15
53
- # => chunk_size >= len(prompts)/15 -> choose ceil(len/15)
54
- from math import ceil
55
- chunk_size = ceil(len(prompts) / 15)
56
- combined_prompts: list[str] = []
57
- for i in range(0, len(prompts), chunk_size):
58
- combined_prompts.append("\nTargeted Locations:\n".join(prompts[i:i+chunk_size]))
59
- combined_prompts = [prefix + "\n" + item for item in combined_prompts]
60
- tab_config = launch_agents(repo_root=repo_root, prompts=combined_prompts)
207
+ combined_prompts = _chunk_prompts(raw_prompts, DEFAULT_AGENT_CAP)
208
+ combined_prompts = [prefix + "\n" + p for p in combined_prompts]
209
+
210
+ from machineconfig.utils.options import choose_one_option
211
+ agent_selected = choose_one_option(header="Select agent type", options=get_args(AGENTS))
212
+
213
+ tab_config = launch_agents(
214
+ repo_root=repo_root,
215
+ prompts=combined_prompts,
216
+ agent=agent_selected,
217
+ max_agents=DEFAULT_AGENT_CAP,
218
+ )
219
+ if not tab_config:
220
+ return
221
+
61
222
  from machineconfig.utils.utils2 import randstr
62
223
  random_name = randstr(length=3)
63
224
  manager = ZellijLocalManager(session2zellij_tabs={"Agents": tab_config}, session_name_prefix=random_name)
@@ -65,5 +226,5 @@ def main():
65
226
  manager.run_monitoring_routine()
66
227
 
67
228
 
68
- if __name__ == "__main__":
229
+ if __name__ == "__main__": # pragma: no cover
69
230
  main()