machineconfig 1.97__py3-none-any.whl → 2.0__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 (166) hide show
  1. machineconfig/cluster/cloud_manager.py +22 -26
  2. machineconfig/cluster/data_transfer.py +2 -2
  3. machineconfig/cluster/distribute.py +0 -2
  4. machineconfig/cluster/file_manager.py +4 -4
  5. machineconfig/cluster/job_params.py +1 -1
  6. machineconfig/cluster/loader_runner.py +8 -8
  7. machineconfig/cluster/remote_machine.py +4 -4
  8. machineconfig/cluster/script_execution.py +2 -2
  9. machineconfig/cluster/sessions_managers/archive/create_zellij_template.py +1 -1
  10. machineconfig/cluster/sessions_managers/enhanced_command_runner.py +23 -23
  11. machineconfig/cluster/sessions_managers/wt_local.py +78 -76
  12. machineconfig/cluster/sessions_managers/wt_local_manager.py +91 -91
  13. machineconfig/cluster/sessions_managers/wt_remote.py +39 -39
  14. machineconfig/cluster/sessions_managers/wt_remote_manager.py +94 -91
  15. machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +56 -54
  16. machineconfig/cluster/sessions_managers/wt_utils/process_monitor.py +49 -49
  17. machineconfig/cluster/sessions_managers/wt_utils/remote_executor.py +18 -18
  18. machineconfig/cluster/sessions_managers/wt_utils/session_manager.py +42 -42
  19. machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +36 -36
  20. machineconfig/cluster/sessions_managers/zellij_local.py +43 -46
  21. machineconfig/cluster/sessions_managers/zellij_local_manager.py +139 -120
  22. machineconfig/cluster/sessions_managers/zellij_remote.py +35 -35
  23. machineconfig/cluster/sessions_managers/zellij_remote_manager.py +33 -33
  24. machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +15 -15
  25. machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +25 -26
  26. machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +49 -49
  27. machineconfig/cluster/sessions_managers/zellij_utils/remote_executor.py +5 -5
  28. machineconfig/cluster/sessions_managers/zellij_utils/session_manager.py +15 -15
  29. machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +11 -11
  30. machineconfig/cluster/templates/utils.py +3 -3
  31. machineconfig/jobs/__pycache__/__init__.cpython-311.pyc +0 -0
  32. machineconfig/jobs/python/__pycache__/__init__.cpython-311.pyc +0 -0
  33. machineconfig/jobs/python/__pycache__/python_ve_symlink.cpython-311.pyc +0 -0
  34. machineconfig/jobs/python/check_installations.py +8 -9
  35. machineconfig/jobs/python/python_cargo_build_share.py +2 -2
  36. machineconfig/jobs/python/vscode/link_ve.py +7 -7
  37. machineconfig/jobs/python/vscode/select_interpreter.py +7 -7
  38. machineconfig/jobs/python/vscode/sync_code.py +5 -5
  39. machineconfig/jobs/python_custom_installers/archive/ngrok.py +2 -2
  40. machineconfig/jobs/python_custom_installers/dev/aider.py +3 -3
  41. machineconfig/jobs/python_custom_installers/dev/alacritty.py +3 -3
  42. machineconfig/jobs/python_custom_installers/dev/brave.py +3 -3
  43. machineconfig/jobs/python_custom_installers/dev/bypass_paywall.py +5 -5
  44. machineconfig/jobs/python_custom_installers/dev/code.py +3 -3
  45. machineconfig/jobs/python_custom_installers/dev/cursor.py +9 -9
  46. machineconfig/jobs/python_custom_installers/dev/docker_desktop.py +4 -4
  47. machineconfig/jobs/python_custom_installers/dev/espanso.py +4 -4
  48. machineconfig/jobs/python_custom_installers/dev/goes.py +4 -4
  49. machineconfig/jobs/python_custom_installers/dev/lvim.py +4 -4
  50. machineconfig/jobs/python_custom_installers/dev/nerdfont.py +3 -3
  51. machineconfig/jobs/python_custom_installers/dev/redis.py +3 -3
  52. machineconfig/jobs/python_custom_installers/dev/wezterm.py +3 -3
  53. machineconfig/jobs/python_custom_installers/dev/winget.py +27 -27
  54. machineconfig/jobs/python_custom_installers/docker.py +3 -3
  55. machineconfig/jobs/python_custom_installers/gh.py +7 -7
  56. machineconfig/jobs/python_custom_installers/hx.py +1 -1
  57. machineconfig/jobs/python_custom_installers/warp-cli.py +3 -3
  58. machineconfig/jobs/python_generic_installers/config.json +412 -389
  59. machineconfig/jobs/python_windows_installers/dev/config.json +1 -1
  60. machineconfig/logger.py +50 -0
  61. machineconfig/profile/__pycache__/__init__.cpython-311.pyc +0 -0
  62. machineconfig/profile/__pycache__/create.cpython-311.pyc +0 -0
  63. machineconfig/profile/__pycache__/shell.cpython-311.pyc +0 -0
  64. machineconfig/profile/create.py +23 -16
  65. machineconfig/profile/create_hardlinks.py +8 -8
  66. machineconfig/profile/shell.py +41 -37
  67. machineconfig/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  68. machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
  69. machineconfig/scripts/linux/devops +2 -2
  70. machineconfig/scripts/linux/fire +1 -0
  71. machineconfig/scripts/linux/fire_agents +0 -1
  72. machineconfig/scripts/linux/mcinit +1 -1
  73. machineconfig/scripts/python/__pycache__/__init__.cpython-311.pyc +0 -0
  74. machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc +0 -0
  75. machineconfig/scripts/python/__pycache__/croshell.cpython-311.pyc +0 -0
  76. machineconfig/scripts/python/__pycache__/devops.cpython-311.pyc +0 -0
  77. machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc +0 -0
  78. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-311.pyc +0 -0
  79. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc +0 -0
  80. machineconfig/scripts/python/__pycache__/fire_agents.cpython-311.pyc +0 -0
  81. machineconfig/scripts/python/__pycache__/fire_jobs.cpython-311.pyc +0 -0
  82. machineconfig/scripts/python/__pycache__/repos.cpython-311.pyc +0 -0
  83. machineconfig/scripts/python/ai/__pycache__/init.cpython-311.pyc +0 -0
  84. machineconfig/scripts/python/ai/__pycache__/mcinit.cpython-311.pyc +0 -0
  85. machineconfig/scripts/python/ai/chatmodes/Thinking-Beast-Mode.chatmode.md +337 -0
  86. machineconfig/scripts/python/ai/chatmodes/Ultimate-Transparent-Thinking-Beast-Mode.chatmode.md +644 -0
  87. machineconfig/scripts/python/ai/chatmodes/deepResearch.chatmode.md +81 -0
  88. machineconfig/scripts/python/ai/configs/.gemini/settings.json +81 -0
  89. machineconfig/scripts/python/ai/instructions/python/dev.instructions.md +45 -0
  90. machineconfig/scripts/python/ai/mcinit.py +103 -0
  91. machineconfig/scripts/python/ai/prompts/allLintersAndTypeCheckers.prompt.md +5 -0
  92. machineconfig/scripts/python/ai/prompts/research-report-skeleton.prompt.md +38 -0
  93. machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +47 -0
  94. machineconfig/scripts/python/archive/tmate_conn.py +5 -5
  95. machineconfig/scripts/python/archive/tmate_start.py +3 -3
  96. machineconfig/scripts/python/choose_wezterm_theme.py +2 -2
  97. machineconfig/scripts/python/cloud_copy.py +19 -18
  98. machineconfig/scripts/python/cloud_mount.py +9 -7
  99. machineconfig/scripts/python/cloud_repo_sync.py +11 -11
  100. machineconfig/scripts/python/cloud_sync.py +1 -1
  101. machineconfig/scripts/python/croshell.py +14 -14
  102. machineconfig/scripts/python/devops.py +6 -6
  103. machineconfig/scripts/python/devops_add_identity.py +8 -6
  104. machineconfig/scripts/python/devops_add_ssh_key.py +18 -18
  105. machineconfig/scripts/python/devops_backup_retrieve.py +13 -13
  106. machineconfig/scripts/python/devops_devapps_install.py +3 -3
  107. machineconfig/scripts/python/devops_update_repos.py +1 -1
  108. machineconfig/scripts/python/dotfile.py +2 -2
  109. machineconfig/scripts/python/fire_agents.py +183 -41
  110. machineconfig/scripts/python/fire_jobs.py +17 -17
  111. machineconfig/scripts/python/ftpx.py +2 -2
  112. machineconfig/scripts/python/gh_models.py +94 -94
  113. machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-311.pyc +0 -0
  114. machineconfig/scripts/python/helpers/__pycache__/cloud_helpers.cpython-311.pyc +0 -0
  115. machineconfig/scripts/python/helpers/__pycache__/helpers2.cpython-311.pyc +0 -0
  116. machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-311.pyc +0 -0
  117. machineconfig/scripts/python/helpers/cloud_helpers.py +3 -3
  118. machineconfig/scripts/python/helpers/helpers2.py +1 -1
  119. machineconfig/scripts/python/helpers/helpers4.py +8 -6
  120. machineconfig/scripts/python/helpers/helpers5.py +7 -7
  121. machineconfig/scripts/python/helpers/repo_sync_helpers.py +1 -1
  122. machineconfig/scripts/python/mount_nfs.py +3 -2
  123. machineconfig/scripts/python/mount_nw_drive.py +4 -4
  124. machineconfig/scripts/python/mount_ssh.py +3 -2
  125. machineconfig/scripts/python/repos.py +8 -8
  126. machineconfig/scripts/python/scheduler.py +1 -1
  127. machineconfig/scripts/python/start_slidev.py +8 -7
  128. machineconfig/scripts/python/start_terminals.py +1 -1
  129. machineconfig/scripts/python/viewer.py +40 -40
  130. machineconfig/scripts/python/wifi_conn.py +65 -66
  131. machineconfig/scripts/python/wsl_windows_transfer.py +1 -1
  132. machineconfig/scripts/windows/mcinit.ps1 +1 -1
  133. machineconfig/settings/linters/.ruff.toml +2 -2
  134. machineconfig/settings/shells/ipy/profiles/default/startup/playext.py +71 -71
  135. machineconfig/settings/shells/wt/settings.json +8 -8
  136. machineconfig/setup_linux/web_shortcuts/tmp.sh +2 -0
  137. machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +10 -7
  138. machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +9 -7
  139. machineconfig/utils/ai/browser_user_wrapper.py +5 -5
  140. machineconfig/utils/ai/generate_file_checklist.py +11 -12
  141. machineconfig/utils/ai/url2md.py +1 -1
  142. machineconfig/utils/cloud/onedrive/setup_oauth.py +4 -4
  143. machineconfig/utils/cloud/onedrive/transaction.py +129 -129
  144. machineconfig/utils/code.py +13 -6
  145. machineconfig/utils/installer.py +51 -53
  146. machineconfig/utils/installer_utils/installer_abc.py +21 -10
  147. machineconfig/utils/installer_utils/installer_class.py +42 -16
  148. machineconfig/utils/io_save.py +3 -15
  149. machineconfig/utils/options.py +10 -3
  150. machineconfig/utils/path.py +5 -0
  151. machineconfig/utils/path_reduced.py +201 -149
  152. machineconfig/utils/procs.py +23 -23
  153. machineconfig/utils/scheduling.py +11 -12
  154. machineconfig/utils/ssh.py +270 -0
  155. machineconfig/utils/terminal.py +180 -0
  156. machineconfig/utils/utils.py +1 -2
  157. machineconfig/utils/utils2.py +43 -0
  158. machineconfig/utils/utils5.py +163 -34
  159. machineconfig/utils/ve.py +2 -2
  160. {machineconfig-1.97.dist-info → machineconfig-2.0.dist-info}/METADATA +13 -8
  161. {machineconfig-1.97.dist-info → machineconfig-2.0.dist-info}/RECORD +163 -149
  162. machineconfig/cluster/self_ssh.py +0 -57
  163. machineconfig/scripts/python/ai/init.py +0 -56
  164. machineconfig/scripts/python/ai/rules/python/dev.md +0 -31
  165. {machineconfig-1.97.dist-info → machineconfig-2.0.dist-info}/WHEEL +0 -0
  166. {machineconfig-1.97.dist-info → machineconfig-2.0.dist-info}/top_level.txt +0 -0
@@ -1,63 +1,205 @@
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
+
21
+ AGENTS: TypeAlias = Literal["cursor-agent", "gemini", "crush"]
22
+ TabConfig = dict[str, tuple[str, str]] # tab name -> (cwd, command)
23
+ DEFAULT_AGENT_CAP = 6
24
+
25
+
26
+ def get_gemini_api_keys() -> list[str]:
27
+ from machineconfig.utils.utils2 import read_ini
28
+ config = read_ini(Path.home().joinpath("dotfiles/creds/llm/gemini/api_keys.ini"))
29
+ res: list[str] = []
30
+ for a_section_name in list(config.sections()):
31
+ a_section = config[a_section_name]
32
+ if "api_key" in a_section:
33
+ api_key = a_section["api_key"].strip()
34
+ if api_key:
35
+ res.append(api_key)
36
+ # res = [v for k, v in config.items("api_keys") if k.startswith("key") and v.strip() != ""]
37
+ print(f"Found {len(res)} Gemini API keys configured.")
38
+ return res
39
+
40
+
41
+ def _search_python_files(repo_root: Path, keyword: str) -> list[Path]:
42
+ """Return all Python files under repo_root whose text contains keyword.
43
+
44
+ Errors reading individual files are ignored (decoded with 'ignore').
45
+ """
46
+ py_files = list(repo_root.rglob("*.py"))
47
+ keyword_lower = keyword.lower()
48
+ matches: list[Path] = []
49
+ for f in py_files:
50
+ try:
51
+ if keyword_lower in f.read_text(encoding="utf-8", errors="ignore").lower():
52
+ matches.append(f)
53
+ except OSError:
54
+ # Skip unreadable file
55
+ continue
56
+ return matches
57
+
58
+
59
+ def _write_list_file(target: Path, files: Iterable[Path]) -> None:
60
+ target.parent.mkdir(parents=True, exist_ok=True)
61
+ target.write_text("\n".join(str(f) for f in files), encoding="utf-8")
62
+ def _chunk_prompts(prompts: list[str], max_agents: int) -> list[str]:
63
+ prompts = [p for p in prompts if p.strip() != ""] # drop blank entries
64
+ if len(prompts) <= max_agents:
65
+ return prompts
66
+ print(f"Chunking {len(prompts)} prompts into groups for up to {max_agents} agents because it exceeds the cap.")
67
+ chunk_size = ceil(len(prompts) / max_agents)
68
+ grouped: list[str] = []
69
+ for i in range(0, len(prompts), chunk_size):
70
+ grouped.append("\nTargeted Locations:\n".join(prompts[i : i + chunk_size]))
71
+ return grouped
72
+ def _confirm(message: str, default_no: bool = True) -> bool:
73
+ suffix = "[y/N]" if default_no else "[Y/n]"
74
+ answer = input(f"{message} {suffix} ").strip().lower()
75
+ if answer in {"y", "yes"}:
76
+ return True
77
+ if not default_no and answer == "":
78
+ return True
79
+ return False
80
+
6
81
 
82
+ def launch_agents(repo_root: Path, prompts: list[str], agent: AGENTS, *, max_agents: int = DEFAULT_AGENT_CAP) -> TabConfig:
83
+ """Create tab configuration for a set of agent prompts.
84
+
85
+ If number of prompts exceeds max_agents, ask user for confirmation.
86
+ (Original behavior raised an error; now interactive override.)
87
+ """
88
+ if not prompts:
89
+ raise ValueError("No prompts provided")
90
+
91
+ if len(prompts) > max_agents:
92
+ proceed = _confirm(
93
+ message=(
94
+ f"You are about to launch {len(prompts)} agents which exceeds the cap ({max_agents}). Proceed?"
95
+ )
96
+ )
97
+ if not proceed:
98
+ print("Aborting per user choice.")
99
+ return {}
100
+
101
+ tab_config: TabConfig = {}
102
+ tmp_dir = repo_root / ".ai" / f"tmp_prompts/{randstr()}"
103
+ tmp_dir.mkdir(parents=True, exist_ok=True)
7
104
 
8
- def launch_agents(repo_root: Path, prompts: list[str]):
9
- tab_config: dict[str, tuple[str, str]] = {}
10
105
  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")
106
+ prompt_path = tmp_dir / f"agent{idx}_prompt.txt"
107
+ prompt_path.write_text(a_prompt, encoding="utf-8")
108
+ match agent:
109
+ case "gemini":
110
+ # model = "gemini-2.5-pro"
111
+ # model = "gemini-2.5-flash-lite"
112
+ model = None # auto-select
113
+ if model is None:
114
+ model_arg = ""
115
+ else:
116
+ model_arg = f"--model {shlex.quote(model)}"
117
+ # Need a real shell for the pipeline; otherwise '| gemini ...' is passed as args to 'cat'
118
+ safe_path = shlex.quote(str(prompt_path))
119
+ api_keys = get_gemini_api_keys()
120
+ api_key = api_keys[idx % len(api_keys)] if api_keys else ""
121
+ # Export the environment variable so it's available to subshells
122
+ cmd = f"""
123
+ export GEMINI_API_KEY={shlex.quote(api_key)}
124
+ echo "Using Gemini API key $GEMINI_API_KEY"
125
+ echo "Launching gemini agent with prompt from {shlex.quote(str(prompt_path))}"
126
+ cat {prompt_path}
127
+ GEMINI_API_KEY={shlex.quote(api_key)} bash -lc 'cat {safe_path} | gemini {model_arg} --yolo --prompt'
128
+ """
129
+ case "cursor-agent":
130
+ # As originally implemented
131
+ cmd = f"""
132
+ echo "Launching cursor-agent with prompt from {shlex.quote(str(prompt_path))}"
133
+ cat {prompt_path}
134
+ cursor-agent --print --output-format text < {prompt_path}
135
+ """
136
+ case "crush":
137
+ cmd = f"""
138
+ echo "Launching crush with prompt from {shlex.quote(str(prompt_path))}"
139
+ cat {prompt_path}
140
+ cat {prompt_path} | crush run
141
+ """
142
+ case _:
143
+ raise ValueError(f"Unsupported agent type: {agent}")
144
+ cmd_path = tmp_dir / f"agent{idx}_cmd.sh"
145
+ cmd_path.write_text(cmd, encoding="utf-8")
146
+ fire_cmd = f"bash {shlex.quote(str(cmd_path))}"
147
+ tab_config[f"Agent{idx}"] = (str(repo_root), fire_cmd)
148
+
17
149
  print(f"Launching a template with #{len(tab_config)} agents")
18
150
  return tab_config
19
151
 
20
152
 
21
- def main():
153
+ def main(): # noqa: C901 - (complexity acceptable for CLI glue)
22
154
  repo_root = Path.cwd()
23
155
  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
- ]
156
+
157
+ file_path_input = input("Enter path to target file [press Enter to generate it from searching]: ").strip()
158
+ if file_path_input == "":
159
+ keyword = input("Enter keyword to search recursively for all .py files containing it: ").strip()
160
+ if not keyword:
161
+ print("No keyword supplied. Exiting.")
162
+ return
163
+ matching_files = _search_python_files(repo_root, keyword)
32
164
  if not matching_files:
33
165
  print(f"💥 No .py files found containing keyword: {keyword}")
34
166
  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')
167
+ for idx, mf in enumerate(matching_files):
168
+ print(f"{idx:>3}: {mf}")
169
+ print(f"\nFound {len(matching_files)} .py files containing keyword: {keyword}")
170
+ target_list_file = repo_root / ".ai" / "target_file.txt"
171
+ _write_list_file(target_list_file, matching_files)
39
172
  separator = "\n"
173
+ source_text = target_list_file.read_text(encoding="utf-8", errors="ignore")
40
174
  else:
41
- separator = input("Enter separator [\\n]: ") or "\n"
175
+ target_file_path = Path(file_path_input).expanduser().resolve()
176
+ if not target_file_path.exists() or not target_file_path.is_file():
177
+ print(f"Invalid file path: {target_file_path}")
178
+ return
179
+ separator = input("Enter separator [\\n]: ").strip() or "\n"
180
+ if not target_file_path.exists():
181
+ print(f"File does not exist: {target_file_path}")
182
+ return
183
+ source_text = target_file_path.read_text(encoding="utf-8", errors="ignore")
42
184
 
43
- err_file = Path(file_path).expanduser().absolute().read_text(encoding='utf-8', errors='ignore')
185
+ raw_prompts = source_text.split(separator)
186
+ print(f"Loaded {len(raw_prompts)} raw prompts from source.")
44
187
  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)
188
+ combined_prompts = _chunk_prompts(raw_prompts, DEFAULT_AGENT_CAP)
189
+ combined_prompts = [prefix + "\n" + p for p in combined_prompts]
190
+
191
+ from machineconfig.utils.options import choose_one_option
192
+ agent_selected = choose_one_option(header="Select agent type", options=get_args(AGENTS))
193
+
194
+ tab_config = launch_agents(
195
+ repo_root=repo_root,
196
+ prompts=combined_prompts,
197
+ agent=agent_selected,
198
+ max_agents=DEFAULT_AGENT_CAP,
199
+ )
200
+ if not tab_config:
201
+ return
202
+
61
203
  from machineconfig.utils.utils2 import randstr
62
204
  random_name = randstr(length=3)
63
205
  manager = ZellijLocalManager(session2zellij_tabs={"Agents": tab_config}, session_name_prefix=random_name)
@@ -65,5 +207,5 @@ def main():
65
207
  manager.run_monitoring_routine()
66
208
 
67
209
 
68
- if __name__ == "__main__":
210
+ if __name__ == "__main__": # pragma: no cover
69
211
  main()
@@ -30,8 +30,6 @@ def main() -> None:
30
30
  parser = argparse.ArgumentParser()
31
31
  parser.add_argument("path", nargs='?', type=str, help="The directory containing the jobs", default=".")
32
32
  parser.add_argument("function", nargs='?', type=str, help="Fuction to run", default=None)
33
-
34
- parser.add_argument("--init", "-I", type=str, help="init this repo", default="")
35
33
  parser.add_argument("--ve", "-v", type=str, help="virtual enviroment name", default="")
36
34
  parser.add_argument("--cmd", "-B", action="store_true", help="Create a cmd fire command to launch the the job asynchronously.")
37
35
  parser.add_argument("--interactive", "-i", action="store_true", help="Whether to run the job interactively using IPython")
@@ -58,18 +56,15 @@ def main() -> None:
58
56
  print(f"❌ Failed to parse arguments: {ex}")
59
57
  parser.print_help()
60
58
  raise ex
61
- path_obj = sanitize_path(PathExtended.cwd())
62
-
63
- if args.init != "":
64
- from machineconfig.scripts.python.ai.init import add_ai_configs
65
- add_ai_configs(repo_root=path_obj)
66
-
59
+ path_obj = sanitize_path(args.path)
60
+ # print(f"Passed path sanitied to {path_obj}")
67
61
  if not path_obj.exists():
68
62
  path_obj = match_file_name(sub_string=args.path, search_root=PathExtended.cwd())
69
63
  else: pass
70
64
  if path_obj.is_dir():
71
65
  print(f"🔍 Searching recursively for Python, PowerShell and Shell scripts in directory `{path_obj}`")
72
66
  files = search_for_files_of_interest(path_obj)
67
+ print(f"🔍 Got #{len(files)} results.")
73
68
  choice_file = choose_one_option(options=files, fzf=True)
74
69
  choice_file = PathExtended(choice_file)
75
70
  else:
@@ -113,13 +108,15 @@ def main() -> None:
113
108
  kwargs[item.name] = input(f"Please enter a value for argument `{item.name}` (type = {item.type}) (default = {item.default}) : ") or item.default
114
109
  elif choice_file.suffix == ".sh": # in this case, we choos lines.
115
110
  options = []
116
- for line in choice_file.read_text().splitlines():
111
+ for line in choice_file.read_text(encoding="utf-8").splitlines():
117
112
  if line.startswith("#"): continue
118
113
  if line == "": continue
119
114
  if line.startswith("echo"): continue
120
115
  options.append(line)
121
116
  chosen_lines = display_options(msg="Choose a line to run", options=options, fzf=True, multi=True)
122
- choice_file = PathExtended.tmpfile(suffix=".sh").write_text("\n".join(chosen_lines))
117
+ choice_file = PathExtended.tmpfile(suffix=".sh")
118
+ choice_file.parent.mkdir(parents=True, exist_ok=True)
119
+ choice_file.write_text("\n".join(chosen_lines), encoding="utf-8")
123
120
  choice_function = None
124
121
  else:
125
122
  choice_function = args.function
@@ -163,7 +160,7 @@ def main() -> None:
163
160
  secrets_template["auth"]["redirect_uri"] = host_url
164
161
  secrets_template["auth"]["cookie_secret"] = randstr(35)
165
162
  secrets_template["auth"]["auth0"]["redirect_uri"] = host_url
166
- save_toml(obj=secrets_template, path=secrets_path)
163
+ save_toml(obj=secrets_template, path=secrets_path)
167
164
  except Exception as ex:
168
165
  print(ex)
169
166
  raise ex
@@ -175,7 +172,7 @@ def main() -> None:
175
172
  # exe = f"cd '{choice_file.parent}'; " + exe
176
173
  elif args.interactive is False: exe = "python"
177
174
  elif args.jupyter: exe = "jupyter-lab"
178
- else:
175
+ else:
179
176
  exe = f"ipython -i --no-banner --profile {ipy_profile} "
180
177
  elif choice_file.suffix == ".ps1" or choice_file.suffix == ".sh":
181
178
  exe = "."
@@ -220,7 +217,7 @@ except ImportError as _ex:
220
217
  """ + txt
221
218
  choice_file = PathExtended.tmp().joinpath(f'tmp_scripts/python/{PathExtended(choice_file).parent.name}_{PathExtended(choice_file).stem}_{randstr()}.py')
222
219
  choice_file.parent.mkdir(parents=True, exist_ok=True)
223
- choice_file.write_text(txt)
220
+ choice_file.write_text(txt, encoding="utf-8")
224
221
 
225
222
  # ========================= determining basic command structure: putting together exe & choice_file & choice_function & pdb
226
223
  if args.debug:
@@ -277,13 +274,13 @@ python -m machineconfig.cluster.templates.cli_click --file {choice_file} """
277
274
  # sub_command = f"{command} --idx={an_arg} --idx_max={args.Nprocess}"
278
275
  # if args.optimized:
279
276
  # sub_command = sub_command.replace("python ", "python -OO ")
280
- # sub_command_path = PathExtended.tmpfile(suffix=".sh").write_text(sub_command)
277
+ # sub_command_path = PathExtended.tmpfile(suffix=".sh").write_text(sub_command, encoding="utf-8")
281
278
  # lines.append(f"""zellij action new-pane -- bash {sub_command_path} """)
282
279
  # lines.append("sleep 5") # python tends to freeze if you launch instances within 1 microsecond of each other
283
280
  # command = "\n".join(lines)
284
281
  tab_config = {}
285
282
  for an_arg in range(args.Nprocess):
286
- tab_config[f"tab{an_arg}"] = (str(PathExtended.cwd()), f"uv run python -m fire {choice_file} {choice_function} --idx={an_arg} --idx_max={args.Nprocess}")
283
+ tab_config[f"tab{an_arg}"] = (str(PathExtended.cwd()), f"uv run -m fire {choice_file} {choice_function} --idx={an_arg} --idx_max={args.Nprocess}")
287
284
  from machineconfig.cluster.sessions_managers.zellij_local import run_zellij_layout
288
285
  run_zellij_layout(tab_config=tab_config, session_name=None)
289
286
  return None
@@ -300,7 +297,9 @@ python -m machineconfig.cluster.templates.cli_click --file {choice_file} """
300
297
  console = Console()
301
298
 
302
299
  if args.zellij_tab is not None:
303
- comman_path__ = PathExtended.tmpfile(suffix=".sh").write_text(command)
300
+ comman_path__ = PathExtended.tmpfile(suffix=".sh")
301
+ comman_path__.parent.mkdir(parents=True, exist_ok=True)
302
+ comman_path__.write_text(command, encoding="utf-8")
304
303
  console.print(Panel(Syntax(command, lexer="shell"), title=f"🔥 fire command @ {comman_path__}: "), style="bold red")
305
304
  import subprocess
306
305
  existing_tab_names = subprocess.run(["zellij", "action", "query-tab-names"], capture_output=True, text=True, check=True).stdout.splitlines()
@@ -335,7 +334,8 @@ python -m machineconfig.cluster.templates.cli_click --file {choice_file} """
335
334
  raise NotImplementedError(f"Platform {platform.system()} not supported.")
336
335
  command = command + f"\n. {program_path}"
337
336
  console.print(Panel(Syntax(command, lexer="shell"), title=f"🔥 fire command @ {program_path}: "), style="bold red")
338
- program_path.write_text(command)
337
+ program_path.parent.mkdir(parents=True, exist_ok=True)
338
+ program_path.write_text(command, encoding="utf-8")
339
339
 
340
340
 
341
341
  if __name__ == '__main__':
@@ -6,7 +6,7 @@ Currently, the only way to work around this is to predifine the host in ~/.ssh/c
6
6
  """
7
7
 
8
8
  import argparse
9
- from crocodile.meta import SSH
9
+ from machineconfig.utils.ssh import SSH
10
10
  from machineconfig.utils.path_reduced import P as PathExtended
11
11
  from machineconfig.scripts.python.helpers.helpers2 import ES
12
12
  from machineconfig.utils.utils2 import pprint
@@ -86,7 +86,7 @@ def main():
86
86
  ┌────────────────────────────────────────────────────────────────
87
87
  │ 🔑 Authentication Failed
88
88
  │ Trying manual authentication...
89
-
89
+
90
90
  │ ⚠️ Caution: Ensure that username is passed appropriately
91
91
  │ This exception only handles password authentication
92
92
  └────────────────────────────────────────────────────────────────""")
@@ -1,104 +1,104 @@
1
- # as per https://github.com/marketplace/models/azure-openai/o1-preview
2
- from openai import OpenAI
3
- from machineconfig.utils.path_reduced import P as PathExtended
4
- from machineconfig.utils.utils2 import read_ini
5
- from rich import print as rprint
6
- from rich.panel import Panel
7
- from typing import Any
1
+ # # as per https://github.com/marketplace/models/azure-openai/o1-preview
2
+ # from openai import OpenAI
3
+ # from machineconfig.utils.path_reduced import P as PathExtended
4
+ # from machineconfig.utils.utils2 import read_ini
5
+ # from rich import print as rprint
6
+ # from rich.panel import Panel
7
+ # from typing import Any
8
8
 
9
9
 
10
- gh_token = read_ini(PathExtended.home().joinpath("dotfiles/creds/git/git_host_tokens.ini"))['thisismygitrepo']['newLongterm']
11
- endpoint = "https://models.inference.ai.azure.com"
12
- model_name_preferences = ["o3-mini", "o1-preview", "o1-mini", "GPT-4o", "GPT-4-o-mini"]
13
- client__ = OpenAI(
14
- base_url=endpoint,
15
- api_key=gh_token,
16
- )
10
+ # gh_token = read_ini(PathExtended.home().joinpath("dotfiles/creds/git/git_host_tokens.ini"))['thisismygitrepo']['newLongterm']
11
+ # endpoint = "https://models.inference.ai.azure.com"
12
+ # model_name_preferences = ["o3-mini", "o1-preview", "o1-mini", "GPT-4o", "GPT-4-o-mini"]
13
+ # client__ = OpenAI(
14
+ # base_url=endpoint,
15
+ # api_key=gh_token,
16
+ # )
17
17
 
18
18
 
19
- def get_response(client: Any, model_name: str, messages: list[dict[str, str]]):
20
- print(f"""
21
- ┌────────────────────────────────────────────────────────────────
22
- │ 🤖 Querying Model: {model_name}
23
- │ Sending request to API...
24
- └────────────────────────────────────────────────────────────────""")
25
- try:
26
- response = client.chat.completions.create(
27
- messages=messages,
28
- model=model_name
29
- )
30
- return response.choices
31
- except Exception as e:
32
- print(f"""
33
- ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
34
- ┃ ❌ API Error with model {model_name}
35
- ┃ {str(e)}
36
- ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━""")
37
- return None
19
+ # def get_response(client: Any, model_name: str, messages: list[dict[str, str]]):
20
+ # print(f"""
21
+ # ┌────────────────────────────────────────────────────────────────
22
+ # │ 🤖 Querying Model: {model_name}
23
+ # │ Sending request to API...
24
+ # └────────────────────────────────────────────────────────────────""")
25
+ # try:
26
+ # response = client.chat.completions.create(
27
+ # messages=messages,
28
+ # model=model_name
29
+ # )
30
+ # return response.choices
31
+ # except Exception as e:
32
+ # print(f"""
33
+ # ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
34
+ # ┃ ❌ API Error with model {model_name}
35
+ # ┃ {str(e)}
36
+ # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━""")
37
+ # return None
38
38
 
39
39
 
40
- def interactive_chat():
41
- conversation_history = []
42
- model_index = 0
43
- model_name = model_name_preferences[model_index]
44
-
45
- print("""
46
- ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
47
- ┃ 🚀 Interactive Chat Started
48
- ┃ Type your message and press Enter to chat
49
- ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━""")
50
-
51
- while True:
52
- header = f" 🤖 Using Model: {model_name} "
53
- print(f"\n{header.center(80, '═')}\n")
54
-
55
- while True:
56
- try:
57
- user_input = input("💬 You: ")
58
- conversation_history.append({"role": "user", "content": user_input})
40
+ # def interactive_chat():
41
+ # conversation_history = []
42
+ # model_index = 0
43
+ # model_name = model_name_preferences[model_index]
59
44
 
60
- while True:
61
- choices = get_response(client__, model_name, conversation_history)
62
- if choices is None:
63
- model_index += 1
64
- model_name = model_name_preferences[model_index % len(model_name_preferences)]
65
- print(f"""
66
- ┌────────────────────────────────────────────────────────────────
67
- │ 🔄 Model Switch
68
- │ Now using: {model_name}
69
- └────────────────────────────────────────────────────────────────""")
70
- continue
71
- else:
72
- break
45
+ # print("""
46
+ # ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
47
+ # 🚀 Interactive Chat Started
48
+ # ┃ Type your message and press Enter to chat
49
+ # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━""")
73
50
 
74
- for a_choice in choices:
75
- response_content = a_choice.message.content
76
- print("\n" * 2)
77
- try:
78
- rprint(Panel(
79
- f"{response_content}",
80
- title=f"🤖 AI ({model_name})",
81
- border_style="blue"
82
- ))
83
- except Exception:
84
- # Fallback if rich formatting fails
85
- print(f"""
86
- ┌────────────────────────────────────────────────────────────────
87
- │ 🤖 AI ({model_name}):
88
-
89
- {response_content}
90
- └────────────────────────────────────────────────────────────────""")
91
-
92
- conversation_history.append({"role": "assistant", "content": response_content})
93
- print("\n")
94
- except KeyboardInterrupt:
95
- print("""
96
- ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
97
- ┃ 👋 Chat Session Ended
98
- ┃ Thank you for using the interactive chat!
99
- ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━""")
100
- return
51
+ # while True:
52
+ # header = f" 🤖 Using Model: {model_name} "
53
+ # print(f"\n{header.center(80, '═')}\n")
101
54
 
55
+ # while True:
56
+ # try:
57
+ # user_input = input("💬 You: ")
58
+ # conversation_history.append({"role": "user", "content": user_input})
102
59
 
103
- if __name__ == "__main__":
104
- interactive_chat()
60
+ # while True:
61
+ # choices = get_response(client__, model_name, conversation_history)
62
+ # if choices is None:
63
+ # model_index += 1
64
+ # model_name = model_name_preferences[model_index % len(model_name_preferences)]
65
+ # print(f"""
66
+ # ┌────────────────────────────────────────────────────────────────
67
+ # │ 🔄 Model Switch
68
+ # │ Now using: {model_name}
69
+ # └────────────────────────────────────────────────────────────────""")
70
+ # continue
71
+ # else:
72
+ # break
73
+
74
+ # for a_choice in choices:
75
+ # response_content = a_choice.message.content
76
+ # print("\n" * 2)
77
+ # try:
78
+ # rprint(Panel(
79
+ # f"{response_content}",
80
+ # title=f"🤖 AI ({model_name})",
81
+ # border_style="blue"
82
+ # ))
83
+ # except Exception:
84
+ # # Fallback if rich formatting fails
85
+ # print(f"""
86
+ # ┌────────────────────────────────────────────────────────────────
87
+ # │ 🤖 AI ({model_name}):
88
+ # │
89
+ # {response_content}
90
+ # └────────────────────────────────────────────────────────────────""")
91
+
92
+ # conversation_history.append({"role": "assistant", "content": response_content})
93
+ # print("\n")
94
+ # except KeyboardInterrupt:
95
+ # print("""
96
+ # ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
97
+ # ┃ 👋 Chat Session Ended
98
+ # ┃ Thank you for using the interactive chat!
99
+ # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━""")
100
+ # return
101
+
102
+
103
+ # if __name__ == "__main__":
104
+ # interactive_chat()
@@ -79,7 +79,7 @@ def absolute(path: str) -> Path:
79
79
  def get_secure_share_cloud_config(interactive: bool, cloud: Optional[str]) -> Args:
80
80
  console = Console()
81
81
  console.print(Panel("🔐 Secure Share Cloud Configuration", expand=False))
82
-
82
+
83
83
  if cloud is None:
84
84
  if os.environ.get("CLOUD_CONFIG_NAME") is not None:
85
85
  default_cloud = os.environ.get("CLOUD_CONFIG_NAME")
@@ -100,7 +100,7 @@ def get_secure_share_cloud_config(interactive: bool, cloud: Optional[str]) -> Ar
100
100
 
101
101
  default_password_path = Path.home().joinpath("dotfiles/creds/passwords/quick_password")
102
102
  if default_password_path.exists():
103
- pwd = default_password_path.read_text().strip()
103
+ pwd = default_password_path.read_text(encoding="utf-8").strip()
104
104
  default_message = "defaults to quick_password"
105
105
  else:
106
106
  pwd = ""
@@ -110,7 +110,7 @@ def get_secure_share_cloud_config(interactive: bool, cloud: Optional[str]) -> Ar
110
110
  pwd=pwd, encrypt=True,
111
111
  zip=True, overwrite=True, share=True,
112
112
  rel2home=True, root="myshare", os_specific=False,)
113
-
113
+
114
114
  display_success("Using SecureShare cloud config")
115
115
  pprint(res.__dict__, "SecureShare Config")
116
116
  return res
@@ -48,7 +48,7 @@ def parse_cloud_source_target(args: Args, source: str, target: str) -> tuple[str
48
48
  # consider activating it by a flag, and also not not overriding explicitly passed args options.
49
49
  assert ES not in target, "Not Implemented here yet."
50
50
  path = absolute(target)
51
- if maybe_config is None:
51
+ if maybe_config is None:
52
52
  tmp_maybe_config: Optional[Args] = find_cloud_config(path=path)
53
53
  maybe_config = tmp_maybe_config
54
54