machineconfig 2.0__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 (235) hide show
  1. machineconfig/cluster/cloud_manager.py +0 -3
  2. machineconfig/cluster/data_transfer.py +0 -1
  3. machineconfig/cluster/file_manager.py +0 -1
  4. machineconfig/cluster/job_params.py +0 -3
  5. machineconfig/cluster/loader_runner.py +0 -3
  6. machineconfig/cluster/remote_machine.py +0 -1
  7. machineconfig/cluster/script_notify_upon_completion.py +0 -1
  8. machineconfig/cluster/sessions_managers/archive/create_zellij_template.py +3 -5
  9. machineconfig/cluster/sessions_managers/archive/session_managers.py +0 -1
  10. machineconfig/cluster/sessions_managers/enhanced_command_runner.py +17 -57
  11. machineconfig/cluster/sessions_managers/wt_local.py +36 -110
  12. machineconfig/cluster/sessions_managers/wt_local_manager.py +42 -112
  13. machineconfig/cluster/sessions_managers/wt_remote.py +23 -30
  14. machineconfig/cluster/sessions_managers/wt_remote_manager.py +20 -62
  15. machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +10 -15
  16. machineconfig/cluster/sessions_managers/wt_utils/process_monitor.py +27 -127
  17. machineconfig/cluster/sessions_managers/wt_utils/remote_executor.py +10 -43
  18. machineconfig/cluster/sessions_managers/wt_utils/session_manager.py +22 -101
  19. machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +11 -39
  20. machineconfig/cluster/sessions_managers/zellij_local.py +49 -102
  21. machineconfig/cluster/sessions_managers/zellij_local_manager.py +34 -78
  22. machineconfig/cluster/sessions_managers/zellij_remote.py +17 -24
  23. machineconfig/cluster/sessions_managers/zellij_remote_manager.py +7 -13
  24. machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +4 -2
  25. machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +6 -6
  26. machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +18 -88
  27. machineconfig/cluster/sessions_managers/zellij_utils/remote_executor.py +2 -6
  28. machineconfig/cluster/sessions_managers/zellij_utils/session_manager.py +12 -40
  29. machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +3 -2
  30. machineconfig/cluster/templates/cli_click.py +0 -1
  31. machineconfig/cluster/templates/cli_gooey.py +0 -2
  32. machineconfig/cluster/templates/cli_trogon.py +0 -1
  33. machineconfig/cluster/templates/run_cloud.py +0 -1
  34. machineconfig/cluster/templates/run_cluster.py +0 -1
  35. machineconfig/cluster/templates/run_remote.py +0 -1
  36. machineconfig/cluster/templates/utils.py +26 -10
  37. machineconfig/jobs/__pycache__/__init__.cpython-313.pyc +0 -0
  38. machineconfig/jobs/linux/msc/cli_agents.sh +16 -0
  39. machineconfig/jobs/python/check_installations.py +1 -0
  40. machineconfig/jobs/python/create_bootable_media.py +0 -2
  41. machineconfig/jobs/python/python_ve_symlink.py +9 -11
  42. machineconfig/jobs/python/tasks.py +0 -1
  43. machineconfig/jobs/python/vscode/api.py +5 -5
  44. machineconfig/jobs/python/vscode/link_ve.py +13 -14
  45. machineconfig/jobs/python/vscode/select_interpreter.py +21 -22
  46. machineconfig/jobs/python/vscode/sync_code.py +9 -13
  47. machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  48. machineconfig/jobs/python_custom_installers/archive/ngrok.py +13 -13
  49. machineconfig/jobs/python_custom_installers/dev/aider.py +7 -15
  50. machineconfig/jobs/python_custom_installers/dev/alacritty.py +9 -18
  51. machineconfig/jobs/python_custom_installers/dev/brave.py +10 -19
  52. machineconfig/jobs/python_custom_installers/dev/bypass_paywall.py +8 -15
  53. machineconfig/jobs/python_custom_installers/dev/code.py +14 -21
  54. machineconfig/jobs/python_custom_installers/dev/cursor.py +3 -14
  55. machineconfig/jobs/python_custom_installers/dev/docker_desktop.py +8 -7
  56. machineconfig/jobs/python_custom_installers/dev/espanso.py +15 -19
  57. machineconfig/jobs/python_custom_installers/dev/goes.py +5 -12
  58. machineconfig/jobs/python_custom_installers/dev/lvim.py +9 -17
  59. machineconfig/jobs/python_custom_installers/dev/nerdfont.py +12 -19
  60. machineconfig/jobs/python_custom_installers/dev/redis.py +12 -20
  61. machineconfig/jobs/python_custom_installers/dev/wezterm.py +12 -19
  62. machineconfig/jobs/python_custom_installers/dev/winget.py +5 -23
  63. machineconfig/jobs/python_custom_installers/docker.py +12 -21
  64. machineconfig/jobs/python_custom_installers/gh.py +11 -19
  65. machineconfig/jobs/python_custom_installers/hx.py +32 -16
  66. machineconfig/jobs/python_custom_installers/warp-cli.py +12 -20
  67. machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  68. machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  69. machineconfig/jobs/windows/archive/archive_pygraphviz.ps1 +1 -1
  70. machineconfig/jobs/windows/msc/cli_agents.bat +0 -0
  71. machineconfig/jobs/windows/msc/cli_agents.ps1 +0 -0
  72. machineconfig/jobs/windows/start_terminal.ps1 +1 -1
  73. machineconfig/profile/create.py +29 -22
  74. machineconfig/profile/create_hardlinks.py +26 -19
  75. machineconfig/profile/shell.py +51 -28
  76. machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
  77. machineconfig/scripts/cloud/init.sh +2 -2
  78. machineconfig/scripts/linux/checkout_versions +1 -1
  79. machineconfig/scripts/linux/choose_wezterm_theme +1 -1
  80. machineconfig/scripts/linux/cloud_copy +1 -1
  81. machineconfig/scripts/linux/cloud_manager +1 -1
  82. machineconfig/scripts/linux/cloud_mount +1 -1
  83. machineconfig/scripts/linux/cloud_repo_sync +1 -1
  84. machineconfig/scripts/linux/cloud_sync +1 -1
  85. machineconfig/scripts/linux/croshell +1 -1
  86. machineconfig/scripts/linux/devops +4 -6
  87. machineconfig/scripts/linux/fire +1 -1
  88. machineconfig/scripts/linux/fire_agents +3 -2
  89. machineconfig/scripts/linux/ftpx +1 -1
  90. machineconfig/scripts/linux/gh_models +1 -1
  91. machineconfig/scripts/linux/kill_process +1 -1
  92. machineconfig/scripts/linux/mcinit +1 -1
  93. machineconfig/scripts/linux/repos +1 -1
  94. machineconfig/scripts/linux/scheduler +1 -1
  95. machineconfig/scripts/linux/start_slidev +1 -1
  96. machineconfig/scripts/linux/start_terminals +1 -1
  97. machineconfig/scripts/linux/url2md +1 -1
  98. machineconfig/scripts/linux/warp-cli.sh +122 -0
  99. machineconfig/scripts/linux/wifi_conn +1 -1
  100. machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc +0 -0
  101. machineconfig/scripts/python/__pycache__/croshell.cpython-313.pyc +0 -0
  102. machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc +0 -0
  103. machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-313.pyc +0 -0
  104. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc +0 -0
  105. machineconfig/scripts/python/__pycache__/fire_jobs.cpython-313.pyc +0 -0
  106. machineconfig/scripts/python/ai/__init__.py +0 -0
  107. machineconfig/scripts/python/ai/__pycache__/__init__.cpython-313.pyc +0 -0
  108. machineconfig/scripts/python/ai/__pycache__/generate_files.cpython-313.pyc +0 -0
  109. machineconfig/scripts/python/ai/__pycache__/mcinit.cpython-313.pyc +0 -0
  110. machineconfig/scripts/python/ai/generate_files.py +84 -0
  111. machineconfig/scripts/python/ai/instructions/python/dev.instructions.md +2 -2
  112. machineconfig/scripts/python/ai/mcinit.py +7 -3
  113. machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +10 -5
  114. machineconfig/scripts/python/cloud_copy.py +1 -1
  115. machineconfig/scripts/python/cloud_mount.py +1 -1
  116. machineconfig/scripts/python/cloud_repo_sync.py +4 -4
  117. machineconfig/scripts/python/croshell.py +5 -3
  118. machineconfig/scripts/python/devops_add_identity.py +1 -1
  119. machineconfig/scripts/python/devops_add_ssh_key.py +1 -1
  120. machineconfig/scripts/python/devops_backup_retrieve.py +1 -1
  121. machineconfig/scripts/python/devops_update_repos.py +140 -52
  122. machineconfig/scripts/python/dotfile.py +1 -1
  123. machineconfig/scripts/python/fire_agents.py +28 -9
  124. machineconfig/scripts/python/fire_jobs.py +3 -4
  125. machineconfig/scripts/python/ftpx.py +2 -1
  126. machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-313.pyc +0 -0
  127. machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-313.pyc +0 -0
  128. machineconfig/scripts/python/helpers/helpers2.py +2 -2
  129. machineconfig/scripts/python/helpers/helpers4.py +1 -2
  130. machineconfig/scripts/python/helpers/repo_sync_helpers.py +1 -1
  131. machineconfig/scripts/python/mount_nfs.py +1 -1
  132. machineconfig/scripts/python/mount_ssh.py +1 -1
  133. machineconfig/scripts/python/repos.py +1 -1
  134. machineconfig/scripts/python/start_slidev.py +1 -1
  135. machineconfig/scripts/python/wsl_windows_transfer.py +1 -1
  136. machineconfig/scripts/windows/checkout_version.ps1 +1 -3
  137. machineconfig/scripts/windows/choose_wezterm_theme.ps1 +1 -3
  138. machineconfig/scripts/windows/cloud_copy.ps1 +2 -6
  139. machineconfig/scripts/windows/cloud_manager.ps1 +1 -1
  140. machineconfig/scripts/windows/cloud_repo_sync.ps1 +1 -2
  141. machineconfig/scripts/windows/cloud_sync.ps1 +2 -2
  142. machineconfig/scripts/windows/croshell.ps1 +2 -2
  143. machineconfig/scripts/windows/devops.ps1 +1 -4
  144. machineconfig/scripts/windows/dotfile.ps1 +1 -3
  145. machineconfig/scripts/windows/fire.ps1 +1 -1
  146. machineconfig/scripts/windows/ftpx.ps1 +2 -2
  147. machineconfig/scripts/windows/gpt.ps1 +1 -1
  148. machineconfig/scripts/windows/kill_process.ps1 +1 -2
  149. machineconfig/scripts/windows/mcinit.ps1 +1 -1
  150. machineconfig/scripts/windows/mount_nfs.ps1 +1 -1
  151. machineconfig/scripts/windows/mount_ssh.ps1 +1 -1
  152. machineconfig/scripts/windows/pomodoro.ps1 +1 -1
  153. machineconfig/scripts/windows/py2exe.ps1 +1 -3
  154. machineconfig/scripts/windows/repos.ps1 +1 -1
  155. machineconfig/scripts/windows/scheduler.ps1 +1 -1
  156. machineconfig/scripts/windows/snapshot.ps1 +2 -2
  157. machineconfig/scripts/windows/start_slidev.ps1 +1 -1
  158. machineconfig/scripts/windows/start_terminals.ps1 +1 -1
  159. machineconfig/scripts/windows/wifi_conn.ps1 +1 -1
  160. machineconfig/scripts/windows/wsl_windows_transfer.ps1 +1 -3
  161. machineconfig/settings/lf/linux/lfrc +1 -1
  162. machineconfig/settings/linters/.ruff_cache/.gitignore +2 -0
  163. machineconfig/settings/linters/.ruff_cache/CACHEDIR.TAG +1 -0
  164. machineconfig/settings/lvim/windows/archive/config_additional.lua +1 -1
  165. machineconfig/settings/svim/linux/init.toml +1 -1
  166. machineconfig/settings/svim/windows/init.toml +1 -1
  167. machineconfig/setup_linux/web_shortcuts/croshell.sh +0 -54
  168. machineconfig/setup_linux/web_shortcuts/interactive.sh +6 -6
  169. machineconfig/setup_windows/web_shortcuts/all.ps1 +2 -2
  170. machineconfig/setup_windows/web_shortcuts/ascii_art.ps1 +1 -1
  171. machineconfig/setup_windows/web_shortcuts/croshell.ps1 +1 -1
  172. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +5 -5
  173. machineconfig/setup_windows/wt_and_pwsh/install_fonts.ps1 +51 -15
  174. machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +66 -12
  175. machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +44 -36
  176. machineconfig/utils/ai/generate_file_checklist.py +8 -10
  177. machineconfig/utils/ai/url2md.py +4 -2
  178. machineconfig/utils/cloud/onedrive/setup_oauth.py +1 -0
  179. machineconfig/utils/cloud/onedrive/transaction.py +63 -98
  180. machineconfig/utils/code.py +60 -39
  181. machineconfig/utils/installer.py +27 -33
  182. machineconfig/utils/installer_utils/installer_abc.py +8 -7
  183. machineconfig/utils/installer_utils/installer_class.py +149 -70
  184. machineconfig/utils/links.py +22 -11
  185. machineconfig/utils/notifications.py +197 -0
  186. machineconfig/utils/options.py +29 -23
  187. machineconfig/utils/path.py +13 -6
  188. machineconfig/utils/path_reduced.py +485 -216
  189. machineconfig/utils/procs.py +47 -41
  190. machineconfig/utils/scheduling.py +0 -1
  191. machineconfig/utils/ssh.py +157 -76
  192. machineconfig/utils/terminal.py +82 -37
  193. machineconfig/utils/utils.py +12 -10
  194. machineconfig/utils/utils2.py +38 -48
  195. machineconfig/utils/utils5.py +183 -116
  196. machineconfig/utils/ve.py +9 -4
  197. {machineconfig-2.0.dist-info → machineconfig-2.1.dist-info}/METADATA +3 -2
  198. {machineconfig-2.0.dist-info → machineconfig-2.1.dist-info}/RECORD +200 -217
  199. machineconfig/jobs/__pycache__/__init__.cpython-311.pyc +0 -0
  200. machineconfig/jobs/python/__pycache__/__init__.cpython-311.pyc +0 -0
  201. machineconfig/jobs/python/__pycache__/python_ve_symlink.cpython-311.pyc +0 -0
  202. machineconfig/jobs/python/archive/python_tools.txt +0 -12
  203. machineconfig/jobs/python/vscode/__pycache__/select_interpreter.cpython-311.pyc +0 -0
  204. machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  205. machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  206. machineconfig/jobs/python_generic_installers/update.py +0 -3
  207. machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  208. machineconfig/profile/__pycache__/__init__.cpython-311.pyc +0 -0
  209. machineconfig/profile/__pycache__/create.cpython-311.pyc +0 -0
  210. machineconfig/profile/__pycache__/shell.cpython-311.pyc +0 -0
  211. machineconfig/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  212. machineconfig/scripts/linux/activate_ve +0 -87
  213. machineconfig/scripts/python/__pycache__/__init__.cpython-311.pyc +0 -0
  214. machineconfig/scripts/python/__pycache__/cloud_copy.cpython-311.pyc +0 -0
  215. machineconfig/scripts/python/__pycache__/cloud_mount.cpython-311.pyc +0 -0
  216. machineconfig/scripts/python/__pycache__/cloud_sync.cpython-311.pyc +0 -0
  217. machineconfig/scripts/python/__pycache__/croshell.cpython-311.pyc +0 -0
  218. machineconfig/scripts/python/__pycache__/devops.cpython-311.pyc +0 -0
  219. machineconfig/scripts/python/__pycache__/devops_backup_retrieve.cpython-311.pyc +0 -0
  220. machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-311.pyc +0 -0
  221. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-311.pyc +0 -0
  222. machineconfig/scripts/python/__pycache__/fire_agents.cpython-311.pyc +0 -0
  223. machineconfig/scripts/python/__pycache__/fire_jobs.cpython-311.pyc +0 -0
  224. machineconfig/scripts/python/__pycache__/get_zellij_cmd.cpython-311.pyc +0 -0
  225. machineconfig/scripts/python/__pycache__/repos.cpython-311.pyc +0 -0
  226. machineconfig/scripts/python/ai/__pycache__/init.cpython-311.pyc +0 -0
  227. machineconfig/scripts/python/ai/__pycache__/mcinit.cpython-311.pyc +0 -0
  228. machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-311.pyc +0 -0
  229. machineconfig/scripts/python/helpers/__pycache__/cloud_helpers.cpython-311.pyc +0 -0
  230. machineconfig/scripts/python/helpers/__pycache__/helpers2.cpython-311.pyc +0 -0
  231. machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-311.pyc +0 -0
  232. machineconfig/scripts/python/helpers/__pycache__/repo_sync_helpers.cpython-311.pyc +0 -0
  233. machineconfig/scripts/windows/activate_ve.ps1 +0 -54
  234. {machineconfig-2.0.dist-info → machineconfig-2.1.dist-info}/WHEEL +0 -0
  235. {machineconfig-2.0.dist-info → machineconfig-2.1.dist-info}/top_level.txt +0 -0
@@ -2,6 +2,7 @@
2
2
  """
3
3
  Status reporting utilities for Windows Terminal layouts and sessions.
4
4
  """
5
+
5
6
  import logging
6
7
  from typing import Dict, Any, Tuple
7
8
  from .process_monitor import WTProcessMonitor
@@ -28,13 +29,7 @@ class WTStatusReporter:
28
29
  return {
29
30
  "wt_session": wt_status,
30
31
  "commands": commands_status,
31
- "summary": {
32
- "total_commands": total_count,
33
- "running_commands": running_count,
34
- "stopped_commands": total_count - running_count,
35
- "session_healthy": wt_status.get("session_exists", False),
36
- "location": wt_status.get("location", "unknown")
37
- }
32
+ "summary": {"total_commands": total_count, "running_commands": running_count, "stopped_commands": total_count - running_count, "session_healthy": wt_status.get("session_exists", False), "location": wt_status.get("location", "unknown")},
38
33
  }
39
34
 
40
35
  def print_status_report(self, tab_config: Dict[str, Tuple[str, str]]) -> None:
@@ -57,11 +52,11 @@ class WTStatusReporter:
57
52
  if wt_session.get("session_exists", False):
58
53
  session_windows = wt_session.get("session_windows", [])
59
54
  all_windows = wt_session.get("all_windows", [])
60
- print(f"✅ Windows Terminal is running")
55
+ print("✅ Windows Terminal is running")
61
56
  print(f" Session windows: {len(session_windows)}")
62
57
  print(f" Total WT windows: {len(all_windows)}")
63
58
  else:
64
- print(f"⚠️ Windows Terminal is running but no session windows found")
59
+ print("⚠️ Windows Terminal is running but no session windows found")
65
60
  else:
66
61
  error_msg = wt_session.get("error", "Unknown error")
67
62
  print(f"❌ Windows Terminal session issue: {error_msg}")
@@ -112,19 +107,10 @@ class WTStatusReporter:
112
107
  wt_windows = self.process_monitor.get_windows_terminal_windows()
113
108
  wt_version = self.session_manager.get_wt_version()
114
109
 
115
- return {
116
- "success": True,
117
- "windows_info": wt_windows,
118
- "version_info": wt_version,
119
- "location": self.process_monitor.location_name
120
- }
110
+ return {"success": True, "windows_info": wt_windows, "version_info": wt_version, "location": self.process_monitor.location_name}
121
111
  except Exception as e:
122
112
  logger.error(f"Failed to get Windows Terminal overview: {e}")
123
- return {
124
- "success": False,
125
- "error": str(e),
126
- "location": self.process_monitor.location_name
127
- }
113
+ return {"success": False, "error": str(e), "location": self.process_monitor.location_name}
128
114
 
129
115
  def print_windows_terminal_overview(self) -> None:
130
116
  """Print an overview of Windows Terminal status."""
@@ -190,39 +176,25 @@ class WTStatusReporter:
190
176
  "stopped_commands": summary["stopped_commands"],
191
177
  "all_commands_running": summary["running_commands"] == summary["total_commands"],
192
178
  "wt_windows_count": len(wt_overview.get("windows_info", {}).get("windows", [])) if wt_overview["success"] else 0,
193
- "wt_version": wt_overview.get("version_info", {}).get("version", "Unknown") if wt_overview["success"] else "Unknown"
179
+ "wt_version": wt_overview.get("version_info", {}).get("version", "Unknown") if wt_overview["success"] else "Unknown",
194
180
  }
195
181
  except Exception as e:
196
182
  logger.error(f"Failed to generate status summary: {e}")
197
- return {
198
- "error": str(e),
199
- "session_name": self.session_manager.session_name,
200
- "location": self.process_monitor.location_name
201
- }
183
+ return {"error": str(e), "session_name": self.session_manager.session_name, "location": self.process_monitor.location_name}
202
184
 
203
185
  def check_tab_specific_status(self, tab_name: str, tab_config: Dict[str, Tuple[str, str]]) -> Dict[str, Any]:
204
186
  """Get detailed status for a specific tab."""
205
187
  if tab_name not in tab_config:
206
- return {
207
- "error": f"Tab '{tab_name}' not found in configuration",
208
- "tab_name": tab_name
209
- }
188
+ return {"error": f"Tab '{tab_name}' not found in configuration", "tab_name": tab_name}
210
189
 
211
190
  try:
212
191
  cmd_status = self.process_monitor.check_command_status(tab_name, tab_config)
213
192
 
214
193
  # Add additional context
215
194
  cwd, command = tab_config[tab_name]
216
- cmd_status["tab_config"] = {
217
- "working_directory": cwd,
218
- "command": command
219
- }
195
+ cmd_status["tab_config"] = {"working_directory": cwd, "command": command}
220
196
 
221
197
  return cmd_status
222
198
  except Exception as e:
223
199
  logger.error(f"Failed to check status for tab '{tab_name}': {e}")
224
- return {
225
- "error": str(e),
226
- "tab_name": tab_name,
227
- "location": self.process_monitor.location_name
228
- }
200
+ return {"error": str(e), "tab_name": tab_name, "location": self.process_monitor.location_name}
@@ -34,13 +34,14 @@ class ZellijLayoutGenerator:
34
34
  @staticmethod
35
35
  def _generate_random_suffix(length: int = 8) -> str:
36
36
  """Generate a random string suffix for unique layout file names."""
37
- return ''.join(random.choices(string.ascii_lowercase + string.digits, k=length))
37
+ return "".join(random.choices(string.ascii_lowercase + string.digits, k=length))
38
38
 
39
39
  @staticmethod
40
40
  def _parse_command(command: str) -> tuple[str, List[str]]:
41
41
  try:
42
42
  parts = shlex.split(command)
43
- if not parts: raise ValueError("Empty command provided")
43
+ if not parts:
44
+ raise ValueError("Empty command provided")
44
45
  return parts[0], parts[1:] if len(parts) > 1 else []
45
46
  except ValueError as e:
46
47
  logger.error(f"Error parsing command '{command}': {e}")
@@ -49,10 +50,11 @@ class ZellijLayoutGenerator:
49
50
 
50
51
  @staticmethod
51
52
  def _format_args_for_kdl(args: List[str]) -> str:
52
- if not args: return ""
53
+ if not args:
54
+ return ""
53
55
  formatted_args = []
54
56
  for arg in args:
55
- if ' ' in arg or '"' in arg or "'" in arg:
57
+ if " " in arg or '"' in arg or "'" in arg:
56
58
  escaped_arg = arg.replace('"', '\\"')
57
59
  formatted_args.append(f'"{escaped_arg}"')
58
60
  else:
@@ -67,17 +69,22 @@ class ZellijLayoutGenerator:
67
69
  escaped_tab_name = tab_name.replace('"', '\\"')
68
70
  tab_section = f' tab name="{escaped_tab_name}" cwd="{tab_cwd}" {{\n'
69
71
  tab_section += f' pane command="{cmd}" {{\n'
70
- if args_str: tab_section += f' args {args_str}\n'
71
- tab_section += ' }\n }\n'
72
+ if args_str:
73
+ tab_section += f" args {args_str}\n"
74
+ tab_section += " }\n }\n"
72
75
  return tab_section
73
76
 
74
77
  @staticmethod
75
78
  def _validate_tab_config(tab_config: Dict[str, tuple[str, str]]) -> None:
76
- if not tab_config: raise ValueError("Tab configuration cannot be empty")
79
+ if not tab_config:
80
+ raise ValueError("Tab configuration cannot be empty")
77
81
  for tab_name, (cwd, command) in tab_config.items():
78
- if not tab_name.strip(): raise ValueError(f"Invalid tab name: {tab_name}")
79
- if not command.strip(): raise ValueError(f"Invalid command for tab '{tab_name}': {command}")
80
- if not cwd.strip(): raise ValueError(f"Invalid cwd for tab '{tab_name}': {cwd}")
82
+ if not tab_name.strip():
83
+ raise ValueError(f"Invalid tab name: {tab_name}")
84
+ if not command.strip():
85
+ raise ValueError(f"Invalid command for tab '{tab_name}': {command}")
86
+ if not cwd.strip():
87
+ raise ValueError(f"Invalid cwd for tab '{tab_name}': {cwd}")
81
88
 
82
89
  def create_zellij_layout(self, tab_config: Dict[str, tuple[str, str]], output_dir: Optional[str] = None, session_name: Optional[str] = None) -> str:
83
90
  ZellijLayoutGenerator._validate_tab_config(tab_config)
@@ -141,14 +148,7 @@ class ZellijLayoutGenerator:
141
148
  @staticmethod
142
149
  def check_command_status(tab_name: str, tab_config: Dict[str, tuple[str, str]]) -> Dict[str, Any]:
143
150
  if tab_name not in tab_config:
144
- return {
145
- "status": "unknown",
146
- "error": f"Tab '{tab_name}' not found in tracked tab config",
147
- "running": False,
148
- "pid": None,
149
- "command": None,
150
- "cwd": None
151
- }
151
+ return {"status": "unknown", "error": f"Tab '{tab_name}' not found in tracked tab config", "running": False, "pid": None, "command": None, "cwd": None}
152
152
 
153
153
  cwd, command = tab_config[tab_name]
154
154
  cmd, _ = ZellijLayoutGenerator._parse_command(command)
@@ -156,51 +156,23 @@ class ZellijLayoutGenerator:
156
156
  try:
157
157
  # Look for processes matching the command
158
158
  matching_processes = []
159
- for proc in psutil.process_iter(['pid', 'name', 'cmdline', 'status']):
159
+ for proc in psutil.process_iter(["pid", "name", "cmdline", "status"]):
160
160
  try:
161
- if proc.info['cmdline'] and len(proc.info['cmdline']) > 0:
161
+ if proc.info["cmdline"] and len(proc.info["cmdline"]) > 0:
162
162
  # Check if the command matches
163
- if (proc.info['name'] == cmd or
164
- cmd in proc.info['cmdline'][0] or
165
- any(cmd in arg for arg in proc.info['cmdline'])):
166
- matching_processes.append({
167
- "pid": proc.info['pid'],
168
- "name": proc.info['name'],
169
- "cmdline": proc.info['cmdline'],
170
- "status": proc.info['status']
171
- })
163
+ if proc.info["name"] == cmd or cmd in proc.info["cmdline"][0] or any(cmd in arg for arg in proc.info["cmdline"]):
164
+ matching_processes.append({"pid": proc.info["pid"], "name": proc.info["name"], "cmdline": proc.info["cmdline"], "status": proc.info["status"]})
172
165
  except (psutil.NoSuchProcess, psutil.AccessDenied):
173
166
  continue
174
167
 
175
168
  if matching_processes:
176
- return {
177
- "status": "running",
178
- "running": True,
179
- "processes": matching_processes,
180
- "command": command,
181
- "cwd": cwd,
182
- "tab_name": tab_name
183
- }
169
+ return {"status": "running", "running": True, "processes": matching_processes, "command": command, "cwd": cwd, "tab_name": tab_name}
184
170
  else:
185
- return {
186
- "status": "not_running",
187
- "running": False,
188
- "processes": [],
189
- "command": command,
190
- "cwd": cwd,
191
- "tab_name": tab_name
192
- }
171
+ return {"status": "not_running", "running": False, "processes": [], "command": command, "cwd": cwd, "tab_name": tab_name}
193
172
 
194
173
  except Exception as e:
195
174
  logger.error(f"Error checking command status for tab '{tab_name}': {e}")
196
- return {
197
- "status": "error",
198
- "error": str(e),
199
- "running": False,
200
- "command": command,
201
- "cwd": cwd,
202
- "tab_name": tab_name
203
- }
175
+ return {"status": "error", "error": str(e), "running": False, "command": command, "cwd": cwd, "tab_name": tab_name}
204
176
 
205
177
  def check_all_commands_status(self) -> Dict[str, Dict[str, Any]]:
206
178
  if not self.tab_config:
@@ -217,48 +189,22 @@ class ZellijLayoutGenerator:
217
189
  def check_zellij_session_status(session_name: str) -> Dict[str, Any]:
218
190
  try:
219
191
  # Run zellij list-sessions command
220
- result = subprocess.run(
221
- ['zellij', 'list-sessions'],
222
- capture_output=True,
223
- text=True,
224
- timeout=10
225
- )
192
+ result = subprocess.run(["zellij", "list-sessions"], capture_output=True, text=True, timeout=10)
226
193
 
227
194
  if result.returncode == 0:
228
- sessions = result.stdout.strip().split('\n') if result.stdout.strip() else []
195
+ sessions = result.stdout.strip().split("\n") if result.stdout.strip() else []
229
196
  session_running = any(session_name in session for session in sessions)
230
197
 
231
- return {
232
- "zellij_running": True,
233
- "session_exists": session_running,
234
- "session_name": session_name,
235
- "all_sessions": sessions
236
- }
198
+ return {"zellij_running": True, "session_exists": session_running, "session_name": session_name, "all_sessions": sessions}
237
199
  else:
238
- return {
239
- "zellij_running": False,
240
- "error": result.stderr,
241
- "session_name": session_name
242
- }
200
+ return {"zellij_running": False, "error": result.stderr, "session_name": session_name}
243
201
 
244
202
  except subprocess.TimeoutExpired:
245
- return {
246
- "zellij_running": False,
247
- "error": "Timeout while checking Zellij sessions",
248
- "session_name": session_name
249
- }
203
+ return {"zellij_running": False, "error": "Timeout while checking Zellij sessions", "session_name": session_name}
250
204
  except FileNotFoundError:
251
- return {
252
- "zellij_running": False,
253
- "error": "Zellij not found in PATH",
254
- "session_name": session_name
255
- }
205
+ return {"zellij_running": False, "error": "Zellij not found in PATH", "session_name": session_name}
256
206
  except Exception as e:
257
- return {
258
- "zellij_running": False,
259
- "error": str(e),
260
- "session_name": session_name
261
- }
207
+ return {"zellij_running": False, "error": str(e), "session_name": session_name}
262
208
 
263
209
  def get_comprehensive_status(self) -> Dict[str, Any]:
264
210
  zellij_status = ZellijLayoutGenerator.check_zellij_session_status(self.session_name or "default")
@@ -270,12 +216,7 @@ class ZellijLayoutGenerator:
270
216
  return {
271
217
  "zellij_session": zellij_status,
272
218
  "commands": commands_status,
273
- "summary": {
274
- "total_commands": total_count,
275
- "running_commands": running_count,
276
- "stopped_commands": total_count - running_count,
277
- "session_healthy": zellij_status.get("session_exists", False)
278
- }
219
+ "summary": {"total_commands": total_count, "running_commands": running_count, "stopped_commands": total_count - running_count, "session_healthy": zellij_status.get("session_exists", False)},
279
220
  }
280
221
 
281
222
  def print_status_report(self) -> None:
@@ -296,7 +237,7 @@ class ZellijLayoutGenerator:
296
237
  else:
297
238
  console.print(f"[bold yellow]⚠️ Zellij is running but session[/bold yellow] [yellow]'{self.session_name}'[/yellow] [yellow]not found[/yellow]")
298
239
  else:
299
- error_msg = zellij.get('error', 'Unknown error')
240
+ error_msg = zellij.get("error", "Unknown error")
300
241
  console.print(f"[bold red]❌ Zellij session issue:[/bold red] [red]{error_msg}[/red]")
301
242
 
302
243
  console.print()
@@ -316,8 +257,8 @@ class ZellijLayoutGenerator:
316
257
  processes = cmd_status.get("processes", [])
317
258
  if processes:
318
259
  proc = processes[0] # Show first process
319
- pid = str(proc.get('pid', 'N/A'))
320
- memory = f"{proc.get('memory_mb', 0):.1f}MB" if proc.get('memory_mb') else "N/A"
260
+ pid = str(proc.get("pid", "N/A"))
261
+ memory = f"{proc.get('memory_mb', 0):.1f}MB" if proc.get("memory_mb") else "N/A"
321
262
  else:
322
263
  pid = "N/A"
323
264
  memory = "N/A"
@@ -326,7 +267,7 @@ class ZellijLayoutGenerator:
326
267
  pid = "N/A"
327
268
  memory = "N/A"
328
269
 
329
- command = cmd_status.get('command', 'Unknown')
270
+ command = cmd_status.get("command", "Unknown")
330
271
  # Truncate long commands
331
272
  if len(command) > 35:
332
273
  command = command[:32] + "..."
@@ -339,29 +280,35 @@ class ZellijLayoutGenerator:
339
280
  # Enhanced summary
340
281
  summary = status["summary"]
341
282
  from rich.panel import Panel
342
- summary_text = f"""[bold]Total commands:[/bold] {summary['total_commands']}
343
- [green]Running:[/green] {summary['running_commands']}
344
- [red]Stopped:[/red] {summary['stopped_commands']}
345
- [yellow]Session healthy:[/yellow] {'✅' if summary['session_healthy'] else '❌'}"""
283
+
284
+ summary_text = f"""[bold]Total commands:[/bold] {summary["total_commands"]}
285
+ [green]Running:[/green] {summary["running_commands"]}
286
+ [red]Stopped:[/red] {summary["stopped_commands"]}
287
+ [yellow]Session healthy:[/yellow] {"✅" if summary["session_healthy"] else "❌"}"""
346
288
 
347
289
  console.print(Panel(summary_text, title="📊 Summary", style="blue"))
348
290
 
291
+
349
292
  def created_zellij_layout(tab_config: Dict[str, tuple[str, str]], output_dir: Optional[str] = None) -> str:
350
293
  generator = ZellijLayoutGenerator()
351
294
  return generator.create_zellij_layout(tab_config, output_dir)
295
+
296
+
352
297
  def run_zellij_layout(tab_config: Dict[str, tuple[str, str]], session_name: Optional[str] = None) -> str:
353
298
  if not session_name:
354
- session_name = ''.join(random.choices(string.ascii_lowercase + string.digits, k=8))
299
+ session_name = "".join(random.choices(string.ascii_lowercase + string.digits, k=8))
355
300
  layout_path = created_zellij_layout(tab_config)
356
301
 
357
302
  # Use enhanced command execution
358
303
  try:
359
304
  from .enhanced_command_runner import enhanced_zellij_session_start
305
+
360
306
  enhanced_zellij_session_start(session_name, layout_path)
361
307
  except ImportError:
362
308
  # Fallback to original implementation
363
309
  cmd = f"zellij delete-session --force {session_name}; zellij --layout {layout_path} a -b {session_name}"
364
310
  import subprocess
311
+
365
312
  subprocess.run(cmd, shell=True, check=True)
366
313
  console.print(f"[bold green]🚀 Zellij layout is running[/bold green] [yellow]@[/yellow] [bold cyan]{session_name}[/bold cyan]")
367
314
  return session_name
@@ -393,7 +340,7 @@ if __name__ == "__main__":
393
340
  "🤖Bot1": ("~/code/bytesense/bithence", "~/scripts/fire -mO go1.py bot1 --kw create_new_bot True"),
394
341
  "🤖Bot2": ("~/code/bytesense/bithence", "~/scripts/fire -mO go2.py bot2 --kw create_new_bot True"),
395
342
  "📊Monitor": ("~", "htop"),
396
- "📝Logs": ("/var/log", "tail -f /var/log/app.log")
343
+ "📝Logs": ("/var/log", "tail -f /var/log/app.log"),
397
344
  }
398
345
  try:
399
346
  # Create layout using the generator directly to access status methods
@@ -90,10 +90,7 @@ class ZellijLocalManager:
90
90
  # 2. Launch new session. We intentionally do NOT wait for completion.
91
91
  # Using the same pattern as before (attach --create) but detached via env var.
92
92
  # ZELLIJ_AUTO_ATTACH=0 prevents auto-attach if compiled with that feature; harmless otherwise.
93
- start_cmd = [
94
- "bash", "-lc",
95
- f"ZELLIJ_AUTO_ATTACH=0 zellij --layout {layout_path} attach {session_name} --create >/dev/null 2>&1 &"
96
- ]
93
+ start_cmd = ["bash", "-lc", f"ZELLIJ_AUTO_ATTACH=0 zellij --layout {layout_path} attach {session_name} --create >/dev/null 2>&1 &"]
97
94
  console.print(f"[bold cyan]🚀 Starting session[/bold cyan] [yellow]'{session_name}'[/yellow] with layout [blue]{layout_path}[/blue] (non-blocking)...")
98
95
  subprocess.Popen(start_cmd)
99
96
 
@@ -133,18 +130,12 @@ class ZellijLocalManager:
133
130
  logger.info(f"Killing session '{session_name}'")
134
131
  result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=10)
135
132
 
136
- results[session_name] = {
137
- "success": result.returncode == 0,
138
- "message": "Session killed" if result.returncode == 0 else result.stderr
139
- }
133
+ results[session_name] = {"success": result.returncode == 0, "message": "Session killed" if result.returncode == 0 else result.stderr}
140
134
 
141
135
  except Exception as e:
142
136
  # Use a fallback key since session_name might not be defined here
143
- key = getattr(manager, 'session_name', None) or f"manager_{self.managers.index(manager)}"
144
- results[key] = {
145
- "success": False,
146
- "error": str(e)
147
- }
137
+ key = getattr(manager, "session_name", None) or f"manager_{self.managers.index(manager)}"
138
+ results[key] = {"success": False, "error": str(e)}
148
139
 
149
140
  return results
150
141
 
@@ -195,12 +186,7 @@ class ZellijLocalManager:
195
186
  status_report[session_name] = {
196
187
  "session_status": session_status,
197
188
  "commands_status": commands_status,
198
- "summary": {
199
- "total_commands": total_count,
200
- "running_commands": running_count,
201
- "stopped_commands": total_count - running_count,
202
- "session_healthy": session_status.get("session_exists", False)
203
- }
189
+ "summary": {"total_commands": total_count, "running_commands": running_count, "stopped_commands": total_count - running_count, "session_healthy": session_status.get("session_exists", False)},
204
190
  }
205
191
 
206
192
  return status_report
@@ -210,12 +196,9 @@ class ZellijLocalManager:
210
196
  all_status = self.check_all_sessions_status()
211
197
 
212
198
  total_sessions = len(all_status)
213
- healthy_sessions = sum(1 for status in all_status.values()
214
- if status["summary"]["session_healthy"])
215
- total_commands = sum(status["summary"]["total_commands"]
216
- for status in all_status.values())
217
- total_running = sum(status["summary"]["running_commands"]
218
- for status in all_status.values())
199
+ healthy_sessions = sum(1 for status in all_status.values() if status["summary"]["session_healthy"])
200
+ total_commands = sum(status["summary"]["total_commands"] for status in all_status.values())
201
+ total_running = sum(status["summary"]["running_commands"] for status in all_status.values())
219
202
 
220
203
  return {
221
204
  "total_sessions": total_sessions,
@@ -225,7 +208,7 @@ class ZellijLocalManager:
225
208
  "running_commands": total_running,
226
209
  "stopped_commands": total_commands - total_running,
227
210
  "all_sessions_healthy": healthy_sessions == total_sessions,
228
- "all_commands_running": total_running == total_commands
211
+ "all_commands_running": total_running == total_commands,
229
212
  }
230
213
 
231
214
  def print_status_report(self) -> None:
@@ -281,6 +264,7 @@ class ZellijLocalManager:
281
264
  Args:
282
265
  wait_ms: How long to wait between checks in milliseconds (default: 30000ms = 30s)
283
266
  """
267
+
284
268
  def routine(scheduler: Scheduler):
285
269
  print(f"\n⏰ Monitoring cycle {scheduler.cycle} at {datetime.now()}")
286
270
  print("-" * 50)
@@ -293,13 +277,15 @@ class ZellijLocalManager:
293
277
  status_data = []
294
278
  for session_name, status in all_status.items():
295
279
  for tab_name, cmd_status in status["commands_status"].items():
296
- status_data.append({
297
- "session": session_name,
298
- "tab": tab_name,
299
- "running": cmd_status.get("running", False),
300
- "command": cmd_status.get("command", "Unknown")[:50] + "..." if len(cmd_status.get("command", "")) > 50 else cmd_status.get("command", ""),
301
- "processes": len(cmd_status.get("processes", []))
302
- })
280
+ status_data.append(
281
+ {
282
+ "session": session_name,
283
+ "tab": tab_name,
284
+ "running": cmd_status.get("running", False),
285
+ "command": cmd_status.get("command", "Unknown")[:50] + "..." if len(cmd_status.get("command", "")) > 50 else cmd_status.get("command", ""),
286
+ "processes": len(cmd_status.get("processes", [])),
287
+ }
288
+ )
303
289
 
304
290
  if status_data:
305
291
  # Format data as table
@@ -346,13 +332,7 @@ class ZellijLocalManager:
346
332
  config_file.write_text(text, encoding="utf-8")
347
333
 
348
334
  # Save metadata
349
- metadata = {
350
- "session_name_prefix": self.session_name_prefix,
351
- "created_at": str(datetime.now()),
352
- "num_managers": len(self.managers),
353
- "sessions": list(self.session2zellij_tabs.keys()),
354
- "manager_type": "ZellijLocalManager"
355
- }
335
+ metadata = {"session_name_prefix": self.session_name_prefix, "created_at": str(datetime.now()), "num_managers": len(self.managers), "sessions": list(self.session2zellij_tabs.keys()), "manager_type": "ZellijLocalManager"}
356
336
  metadata_file = session_dir / "metadata.json"
357
337
  text = json.dumps(metadata, indent=2, ensure_ascii=False)
358
338
  metadata_file.write_text(text, encoding="utf-8")
@@ -362,11 +342,7 @@ class ZellijLocalManager:
362
342
  managers_dir.mkdir(exist_ok=True)
363
343
 
364
344
  for i, manager in enumerate(self.managers):
365
- manager_data = {
366
- "session_name": manager.session_name,
367
- "tab_config": manager.tab_config,
368
- "layout_path": manager.layout_path
369
- }
345
+ manager_data = {"session_name": manager.session_name, "tab_config": manager.tab_config, "layout_path": manager.layout_path}
370
346
  manager_file = managers_dir / f"manager_{i}_{manager.session_name}.json"
371
347
  text = json.dumps(manager_data, indent=2, ensure_ascii=False)
372
348
  manager_file.write_text(text, encoding="utf-8")
@@ -375,7 +351,7 @@ class ZellijLocalManager:
375
351
  return session_id
376
352
 
377
353
  @classmethod
378
- def load(cls, session_id: str) -> 'ZellijLocalManager':
354
+ def load(cls, session_id: str) -> "ZellijLocalManager":
379
355
  """Load a saved manager state from disk."""
380
356
  session_dir = TMP_SERIALIZATION_DIR / session_id
381
357
 
@@ -387,14 +363,14 @@ class ZellijLocalManager:
387
363
  if not config_file.exists():
388
364
  raise FileNotFoundError(f"Configuration file not found: {config_file}")
389
365
 
390
- with open(config_file, 'r', encoding='utf-8') as f:
366
+ with open(config_file, "r", encoding="utf-8") as f:
391
367
  session2zellij_tabs = json.load(f)
392
368
 
393
369
  # Load metadata
394
370
  metadata_file = session_dir / "metadata.json"
395
371
  session_name_prefix = "LocalJobMgr" # default fallback
396
372
  if metadata_file.exists():
397
- with open(metadata_file, 'r', encoding='utf-8') as f:
373
+ with open(metadata_file, "r", encoding="utf-8") as f:
398
374
  metadata = json.load(f)
399
375
  session_name_prefix = metadata.get("session_name_prefix", "LocalJobMgr")
400
376
 
@@ -409,7 +385,7 @@ class ZellijLocalManager:
409
385
 
410
386
  for manager_file in manager_files:
411
387
  try:
412
- with open(manager_file, 'r', encoding='utf-8') as f:
388
+ with open(manager_file, "r", encoding="utf-8") as f:
413
389
  manager_data = json.load(f)
414
390
 
415
391
  # Recreate the manager
@@ -450,6 +426,7 @@ class ZellijLocalManager:
450
426
 
451
427
  try:
452
428
  import shutil
429
+
453
430
  shutil.rmtree(session_dir)
454
431
  logger.info(f"✅ Deleted session: {session_id}")
455
432
  return True
@@ -463,15 +440,10 @@ class ZellijLocalManager:
463
440
 
464
441
  try:
465
442
  # Get all running zellij sessions
466
- result = subprocess.run(
467
- ['zellij', 'list-sessions'],
468
- capture_output=True,
469
- text=True,
470
- timeout=10
471
- )
443
+ result = subprocess.run(["zellij", "list-sessions"], capture_output=True, text=True, timeout=10)
472
444
 
473
445
  if result.returncode == 0:
474
- all_sessions = result.stdout.strip().split('\n') if result.stdout.strip() else []
446
+ all_sessions = result.stdout.strip().split("\n") if result.stdout.strip() else []
475
447
 
476
448
  # Filter to only our managed sessions
477
449
  for manager in self.managers:
@@ -480,12 +452,7 @@ class ZellijLocalManager:
480
452
  continue # Skip managers without a session name
481
453
  is_active = any(session_name in session for session in all_sessions)
482
454
 
483
- active_sessions.append({
484
- "session_name": session_name,
485
- "is_active": is_active,
486
- "tab_count": len(manager.tab_config),
487
- "tabs": list(manager.tab_config.keys())
488
- })
455
+ active_sessions.append({"session_name": session_name, "is_active": is_active, "tab_count": len(manager.tab_config), "tabs": list(manager.tab_config.keys())})
489
456
 
490
457
  except Exception as e:
491
458
  logger.error(f"Error listing active sessions: {e}")
@@ -496,21 +463,9 @@ class ZellijLocalManager:
496
463
  if __name__ == "__main__":
497
464
  # Example usage
498
465
  sample_sessions = {
499
- "development": {
500
- "🚀Frontend": ("~/code/myapp/frontend", "npm run dev"),
501
- "⚙️Backend": ("~/code/myapp/backend", "python manage.py runserver"),
502
- "📊Monitor": ("~", "htop")
503
- },
504
- "testing": {
505
- "🧪Tests": ("~/code/myapp", "pytest --watch"),
506
- "🔍Coverage": ("~/code/myapp", "coverage run --source=. -m pytest"),
507
- "📝Logs": ("~/logs", "tail -f app.log")
508
- },
509
- "deployment": {
510
- "🐳Docker": ("~/code/myapp", "docker-compose up"),
511
- "☸️K8s": ("~/k8s", "kubectl get pods --watch"),
512
- "📈Metrics": ("~", "k9s")
513
- }
466
+ "development": {"🚀Frontend": ("~/code/myapp/frontend", "npm run dev"), "⚙️Backend": ("~/code/myapp/backend", "python manage.py runserver"), "📊Monitor": ("~", "htop")},
467
+ "testing": {"🧪Tests": ("~/code/myapp", "pytest --watch"), "🔍Coverage": ("~/code/myapp", "coverage run --source=. -m pytest"), "📝Logs": ("~/logs", "tail -f app.log")},
468
+ "deployment": {"🐳Docker": ("~/code/myapp", "docker-compose up"), "☸️K8s": ("~/k8s", "kubectl get pods --watch"), "📈Metrics": ("~", "k9s")},
514
469
  }
515
470
 
516
471
  try:
@@ -549,4 +504,5 @@ if __name__ == "__main__":
549
504
  except Exception as e:
550
505
  print(f"❌ Error: {e}")
551
506
  import traceback
552
- traceback.print_exc()
507
+
508
+ traceback.print_exc()