machineconfig 5.21__py3-none-any.whl → 5.23__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 (45) hide show
  1. machineconfig/cluster/sessions_managers/helpers/zellij_local_helper.py +298 -0
  2. machineconfig/cluster/sessions_managers/helpers/zellij_local_helper_restart.py +77 -0
  3. machineconfig/cluster/sessions_managers/helpers/zellij_local_helper_with_panes.py +228 -0
  4. machineconfig/cluster/sessions_managers/helpers/zellij_local_manager_helper.py +165 -0
  5. machineconfig/cluster/sessions_managers/wt_local.py +100 -75
  6. machineconfig/cluster/sessions_managers/wt_local_manager.py +17 -21
  7. machineconfig/cluster/sessions_managers/wt_remote.py +51 -43
  8. machineconfig/cluster/sessions_managers/wt_remote_manager.py +16 -8
  9. machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +6 -19
  10. machineconfig/cluster/sessions_managers/zellij_local.py +79 -371
  11. machineconfig/cluster/sessions_managers/zellij_local_manager.py +20 -168
  12. machineconfig/cluster/sessions_managers/zellij_remote.py +38 -39
  13. machineconfig/cluster/sessions_managers/zellij_remote_manager.py +13 -10
  14. machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +4 -1
  15. machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +5 -20
  16. machineconfig/profile/shell.py +1 -1
  17. machineconfig/scripts/python/ai/scripts/lint_and_type_check.ps1 +17 -17
  18. machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +17 -17
  19. machineconfig/scripts/python/ai/solutions/copilot/instructions/python/dev.instructions.md +1 -1
  20. machineconfig/scripts/python/ai/solutions/copilot/prompts/pyright_fix.md +16 -0
  21. machineconfig/scripts/python/ai/solutions/generic.py +15 -4
  22. machineconfig/scripts/python/cloud_repo_sync.py +26 -25
  23. machineconfig/scripts/python/count_lines.py +6 -6
  24. machineconfig/scripts/python/croshell.py +2 -4
  25. machineconfig/scripts/python/devops.py +7 -2
  26. machineconfig/scripts/python/devops_status.py +521 -0
  27. machineconfig/scripts/python/devops_update_repos.py +1 -1
  28. machineconfig/scripts/python/fire_agents_help_launch.py +6 -1
  29. machineconfig/scripts/python/fire_jobs_args_helper.py +4 -1
  30. machineconfig/scripts/python/helpers/repo_sync_helpers.py +0 -43
  31. machineconfig/scripts/python/onetimeshare.py +0 -1
  32. machineconfig/scripts/windows/share_smb.ps1 +0 -6
  33. machineconfig/setup_linux/repos.sh +1 -29
  34. machineconfig/setup_windows/repos.ps1 +0 -12
  35. machineconfig/utils/files/read.py +4 -6
  36. machineconfig/utils/notifications.py +1 -1
  37. machineconfig/utils/source_of_truth.py +1 -1
  38. machineconfig/utils/ssh.py +2 -13
  39. {machineconfig-5.21.dist-info → machineconfig-5.23.dist-info}/METADATA +1 -1
  40. {machineconfig-5.21.dist-info → machineconfig-5.23.dist-info}/RECORD +43 -39
  41. machineconfig/cluster/sessions_managers/ffile.py +0 -4
  42. machineconfig/scripts/python/ai/solutions/copilot/prompts/allLintersAndTypeCheckers.prompt.md +0 -5
  43. {machineconfig-5.21.dist-info → machineconfig-5.23.dist-info}/WHEEL +0 -0
  44. {machineconfig-5.21.dist-info → machineconfig-5.23.dist-info}/entry_points.txt +0 -0
  45. {machineconfig-5.21.dist-info → machineconfig-5.23.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,521 @@
1
+ """Machine Status Display - Comprehensive system and configuration overview"""
2
+
3
+ import platform
4
+ import shutil
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from rich.console import Console
9
+ from rich.panel import Panel
10
+ from rich.table import Table
11
+ from rich.text import Text
12
+
13
+ from machineconfig.utils.path_extended import PathExtended
14
+ from machineconfig.utils.source_of_truth import CONFIG_PATH, DEFAULTS_PATH, LIBRARY_ROOT
15
+
16
+
17
+ console = Console()
18
+
19
+
20
+ def _check_system_info() -> dict[str, str]:
21
+ """Gather basic system information."""
22
+ import socket
23
+ import os
24
+
25
+ return {
26
+ "hostname": socket.gethostname(),
27
+ "system": platform.system(),
28
+ "release": platform.release(),
29
+ "version": platform.version(),
30
+ "machine": platform.machine(),
31
+ "processor": platform.processor() or "Unknown",
32
+ "python_version": platform.python_version(),
33
+ "user": os.getenv("USER") or os.getenv("USERNAME") or "Unknown",
34
+ }
35
+
36
+
37
+ def _check_shell_profile_status() -> dict[str, Any]:
38
+ """Check shell profile configuration status."""
39
+ from machineconfig.profile.shell import get_shell_profile_path
40
+
41
+ try:
42
+ profile_path = get_shell_profile_path()
43
+ profile_exists = profile_path.exists()
44
+ profile_content = profile_path.read_text(encoding="utf-8") if profile_exists else ""
45
+
46
+ system_name = platform.system()
47
+ if system_name == "Windows":
48
+ init_script = PathExtended(LIBRARY_ROOT).joinpath("settings/shells/pwsh/init.ps1")
49
+ init_script_copy = PathExtended(CONFIG_PATH).joinpath("profile/init.ps1").collapseuser()
50
+ source_reference = f". {str(init_script.collapseuser()).replace('~', '$HOME')}"
51
+ source_copy = f". {str(init_script_copy).replace('~', '$HOME')}"
52
+ else:
53
+ init_script = PathExtended(LIBRARY_ROOT).joinpath("settings/shells/bash/init.sh")
54
+ init_script_copy = PathExtended(CONFIG_PATH).joinpath("profile/init.sh").collapseuser()
55
+ source_reference = f"source {str(init_script.collapseuser()).replace('~', '$HOME')}"
56
+ source_copy = f"source {str(init_script_copy).replace('~', '$HOME')}"
57
+
58
+ configured = source_reference in profile_content or source_copy in profile_content
59
+ method = "reference" if source_reference in profile_content else ("copy" if source_copy in profile_content else "none")
60
+
61
+ return {
62
+ "profile_path": str(profile_path),
63
+ "exists": profile_exists,
64
+ "configured": configured,
65
+ "method": method,
66
+ "init_script_exists": init_script.exists(),
67
+ "init_script_copy_exists": init_script_copy.exists(),
68
+ }
69
+ except Exception as ex:
70
+ return {"profile_path": "Error", "exists": False, "configured": False, "method": "error", "error": str(ex), "init_script_exists": False, "init_script_copy_exists": False}
71
+
72
+
73
+ def _check_machineconfig_repo() -> dict[str, Any]:
74
+ """Check machineconfig repository status."""
75
+ repo_path = Path.home().joinpath("code/machineconfig")
76
+ if not repo_path.exists():
77
+ return {"exists": False, "is_repo": False, "clean": False, "branch": "N/A", "commit": "N/A", "remotes": []}
78
+
79
+ try:
80
+ import git
81
+
82
+ repo = git.Repo(str(repo_path))
83
+ is_dirty = repo.is_dirty(untracked_files=True)
84
+ current_branch = repo.active_branch.name if not repo.head.is_detached else "DETACHED"
85
+ current_commit = repo.head.commit.hexsha[:8]
86
+ remotes = [remote.name for remote in repo.remotes]
87
+
88
+ return {
89
+ "exists": True,
90
+ "is_repo": True,
91
+ "clean": not is_dirty,
92
+ "branch": current_branch,
93
+ "commit": current_commit,
94
+ "remotes": remotes,
95
+ "path": str(repo_path),
96
+ }
97
+ except Exception as ex:
98
+ return {"exists": True, "is_repo": False, "clean": False, "branch": "Error", "commit": "N/A", "remotes": [], "error": str(ex)}
99
+
100
+
101
+ def _check_repos_status() -> dict[str, Any]:
102
+ """Check configured repositories status."""
103
+ from machineconfig.utils.io import read_ini
104
+
105
+ try:
106
+ repos_str = read_ini(DEFAULTS_PATH)["general"]["repos"]
107
+ repo_paths = [Path(p.strip()).expanduser() for p in repos_str.split(",") if p.strip()]
108
+
109
+ repos_info = []
110
+ for repo_path in repo_paths:
111
+ if not repo_path.exists():
112
+ repos_info.append({"path": str(repo_path), "name": repo_path.name, "exists": False, "is_repo": False})
113
+ continue
114
+
115
+ try:
116
+ import git
117
+
118
+ repo = git.Repo(str(repo_path))
119
+ repos_info.append({"path": str(repo_path), "name": repo_path.name, "exists": True, "is_repo": True, "clean": not repo.is_dirty(untracked_files=True), "branch": repo.active_branch.name if not repo.head.is_detached else "DETACHED"})
120
+ except Exception:
121
+ repos_info.append({"path": str(repo_path), "name": repo_path.name, "exists": True, "is_repo": False})
122
+
123
+ return {"configured": True, "count": len(repos_info), "repos": repos_info}
124
+ except (FileNotFoundError, KeyError, IndexError):
125
+ return {"configured": False, "count": 0, "repos": []}
126
+
127
+
128
+ def _check_ssh_status() -> dict[str, Any]:
129
+ """Check SSH configuration status."""
130
+ ssh_dir = PathExtended.home().joinpath(".ssh")
131
+ if not ssh_dir.exists():
132
+ return {"ssh_dir_exists": False, "keys": [], "config_exists": False, "authorized_keys_exists": False, "known_hosts_exists": False}
133
+
134
+ keys = []
135
+ for pub_key in ssh_dir.glob("*.pub"):
136
+ private_key = pub_key.with_suffix("")
137
+ keys.append({"name": pub_key.stem, "public_exists": True, "private_exists": private_key.exists(), "public_path": str(pub_key), "private_path": str(private_key)})
138
+
139
+ config_file = ssh_dir.joinpath("config")
140
+ authorized_keys = ssh_dir.joinpath("authorized_keys")
141
+ known_hosts = ssh_dir.joinpath("known_hosts")
142
+
143
+ return {
144
+ "ssh_dir_exists": True,
145
+ "keys": keys,
146
+ "config_exists": config_file.exists(),
147
+ "authorized_keys_exists": authorized_keys.exists(),
148
+ "known_hosts_exists": known_hosts.exists(),
149
+ "ssh_dir_path": str(ssh_dir),
150
+ }
151
+
152
+
153
+ def _check_config_files_status() -> dict[str, Any]:
154
+ """Check public and private configuration files status."""
155
+ from machineconfig.profile.create import read_mapper
156
+
157
+ try:
158
+ mapper = read_mapper()
159
+ public_configs = list(mapper.get("public", {}).keys())
160
+ private_configs = list(mapper.get("private", {}).keys())
161
+
162
+ public_count = len(public_configs)
163
+ private_count = len(private_configs)
164
+
165
+ public_linked = 0
166
+ for config_name in public_configs:
167
+ for config_item in mapper["public"][config_name]:
168
+ target_path = PathExtended(config_item["config_file_default_path"]).expanduser()
169
+ if target_path.exists():
170
+ public_linked += 1
171
+ break
172
+
173
+ private_linked = 0
174
+ for config_name in private_configs:
175
+ for config_item in mapper["private"][config_name]:
176
+ target_path = PathExtended(config_item["config_file_default_path"]).expanduser()
177
+ if target_path.exists():
178
+ private_linked += 1
179
+ break
180
+
181
+ return {
182
+ "public_count": public_count,
183
+ "public_linked": public_linked,
184
+ "private_count": private_count,
185
+ "private_linked": private_linked,
186
+ "public_configs": public_configs,
187
+ "private_configs": private_configs,
188
+ }
189
+ except Exception as ex:
190
+ return {"public_count": 0, "public_linked": 0, "private_count": 0, "private_linked": 0, "error": str(ex), "public_configs": [], "private_configs": []}
191
+
192
+
193
+ def _check_important_tools() -> dict[str, dict[str, bool]]:
194
+ """Check if important CLI tools are installed, organized by groups."""
195
+ from machineconfig.jobs.installer.package_groups import PACKAGE_GROUP2NAMES
196
+
197
+ group_status = {}
198
+ for group_name, tools in PACKAGE_GROUP2NAMES.items():
199
+ tool_status = {}
200
+ for tool in tools:
201
+ tool_status[tool] = shutil.which(tool) is not None
202
+ group_status[group_name] = tool_status
203
+
204
+ return group_status
205
+
206
+
207
+ def _check_backup_config() -> dict[str, Any]:
208
+ """Check backup configuration status."""
209
+ from machineconfig.utils.io import read_ini
210
+ import tomllib
211
+
212
+ try:
213
+ cloud_config = read_ini(DEFAULTS_PATH)["general"]["rclone_config_name"]
214
+ except (FileNotFoundError, KeyError, IndexError):
215
+ cloud_config = "Not configured"
216
+
217
+ try:
218
+ backup_file = LIBRARY_ROOT.joinpath("profile/backup.toml")
219
+ if backup_file.exists():
220
+ backup_data = tomllib.loads(backup_file.read_text(encoding="utf-8"))
221
+ backup_items = list(backup_data.keys())
222
+ backup_items_count = len(backup_items)
223
+ else:
224
+ backup_items = []
225
+ backup_items_count = 0
226
+ except Exception:
227
+ backup_items = []
228
+ backup_items_count = 0
229
+
230
+ return {"cloud_config": cloud_config, "backup_items_count": backup_items_count, "backup_items": backup_items}
231
+
232
+
233
+ def _display_system_info(info: dict[str, str]) -> None:
234
+ """Display system information panel."""
235
+ console.rule("[bold blue]💻 System Information[/bold blue]")
236
+
237
+ table = Table(show_header=False, box=None, padding=(0, 1), expand=False)
238
+ table.add_column("Property", style="cyan", no_wrap=True)
239
+ table.add_column("Value", style="white")
240
+
241
+ table.add_row("🏠 Hostname", info["hostname"])
242
+ table.add_row("💿 System", f"{info['system']} {info['release']}")
243
+ table.add_row("🖥️ Machine", info["machine"])
244
+ table.add_row("⚙️ Processor", info["processor"])
245
+ table.add_row("🐍 Python", info["python_version"])
246
+ table.add_row("👤 User", info["user"])
247
+
248
+ console.print(Panel(table, title="System", border_style="blue", padding=(1, 2), expand=False))
249
+
250
+
251
+ def _display_shell_status(status: dict[str, Any]) -> None:
252
+ """Display shell profile status panel."""
253
+ console.rule("[bold green]🐚 Shell Profile[/bold green]")
254
+
255
+ if "error" in status:
256
+ console.print(Panel(f"❌ Error: {status['error']}", title="Shell Profile", border_style="red", padding=(1, 2), expand=False))
257
+ return
258
+
259
+ from rich.columns import Columns
260
+
261
+ left_table = Table(show_header=False, box=None, padding=(0, 1))
262
+ left_table.add_column("Item", style="cyan", no_wrap=True)
263
+ left_table.add_column("Status")
264
+
265
+ left_table.add_row("📄 Profile", status['profile_path'])
266
+ left_table.add_row(f"{'✅' if status['exists'] else '❌'} Exists", str(status['exists']))
267
+ left_table.add_row(f"{'✅' if status['configured'] else '❌'} Configured", str(status['configured']))
268
+
269
+ right_table = Table(show_header=False, box=None, padding=(0, 1))
270
+ right_table.add_column("Item", style="cyan", no_wrap=True)
271
+ right_table.add_column("Status")
272
+
273
+ right_table.add_row("🔧 Method", status['method'])
274
+ right_table.add_row(f"{'✅' if status['init_script_exists'] else '❌'} Init (source)", str(status['init_script_exists']))
275
+ right_table.add_row(f"{'✅' if status['init_script_copy_exists'] else '❌'} Init (copy)", str(status['init_script_copy_exists']))
276
+
277
+ border_style = "green" if status["configured"] else "yellow"
278
+ console.print(Panel(Columns([left_table, right_table], equal=True, expand=True), title="Shell Profile", border_style=border_style, padding=(1, 2), expand=False))
279
+
280
+
281
+ def _display_machineconfig_repo(info: dict[str, Any]) -> None:
282
+ """Display machineconfig repository status."""
283
+ console.rule("[bold magenta]📦 Machineconfig Repository[/bold magenta]")
284
+
285
+ if not info["exists"]:
286
+ console.print(Panel("❌ Machineconfig repository not found at ~/code/machineconfig", title="Repository Status", border_style="red", padding=(1, 2), expand=False))
287
+ return
288
+
289
+ if not info["is_repo"]:
290
+ console.print(Panel(f"❌ Directory exists but is not a git repository\n{info.get('error', 'Unknown error')}", title="Repository Status", border_style="red", padding=(1, 2), expand=False))
291
+ return
292
+
293
+ table = Table(show_header=False, box=None, padding=(0, 1), expand=False)
294
+ table.add_column("Property", style="cyan", no_wrap=True)
295
+ table.add_column("Value", style="white")
296
+
297
+ table.add_row("📁 Path", info['path'])
298
+ table.add_row("🌿 Branch", info['branch'])
299
+ table.add_row("🔖 Commit", info['commit'])
300
+ table.add_row(f"{'✅' if info['clean'] else '⚠️'} Status", 'Clean' if info['clean'] else 'Uncommitted changes')
301
+ table.add_row("📡 Remotes", ', '.join(info['remotes']) if info['remotes'] else 'None')
302
+
303
+ border_style = "green" if info["clean"] else "yellow"
304
+ console.print(Panel(table, title="Machineconfig Repository", border_style=border_style, padding=(1, 2), expand=False))
305
+
306
+
307
+ def _display_repos_status(status: dict[str, Any]) -> None:
308
+ """Display configured repositories status."""
309
+ console.rule("[bold cyan]📚 Configured Repositories[/bold cyan]")
310
+
311
+ if not status["configured"]:
312
+ console.print(Panel(f"⚠️ No repositories configured in {DEFAULTS_PATH}", title="Repositories", border_style="yellow", padding=(1, 2)))
313
+ return
314
+
315
+ if status["count"] == 0:
316
+ console.print(Panel("ℹ️ No repositories configured", title="Repositories", border_style="blue", padding=(1, 2)))
317
+ return
318
+
319
+ table = Table(show_lines=True, header_style="bold cyan")
320
+ table.add_column("Repository", style="bold")
321
+ table.add_column("Status")
322
+ table.add_column("Details")
323
+
324
+ for repo in status["repos"]:
325
+ name = repo["name"]
326
+ if not repo["exists"]:
327
+ table.add_row(f"❌ {name}", "Missing", f"Path: {repo['path']}")
328
+ elif not repo["is_repo"]:
329
+ table.add_row(f"⚠️ {name}", "Not a repo", f"Path: {repo['path']}")
330
+ else:
331
+ status_icon = "✅" if repo["clean"] else "⚠️"
332
+ status_text = "Clean" if repo["clean"] else "Uncommitted changes"
333
+ table.add_row(f"{status_icon} {name}", status_text, f"Branch: {repo['branch']}")
334
+
335
+ console.print(Panel(table, title=f"Repositories ({status['count']})", border_style="cyan", padding=(1, 2)))
336
+
337
+
338
+ def _display_ssh_status(status: dict[str, Any]) -> None:
339
+ """Display SSH configuration status."""
340
+ console.rule("[bold yellow]🔐 SSH Configuration[/bold yellow]")
341
+
342
+ if not status["ssh_dir_exists"]:
343
+ console.print(Panel("❌ SSH directory (~/.ssh) does not exist", title="SSH Status", border_style="red", padding=(1, 2), expand=False))
344
+ return
345
+
346
+ from rich.columns import Columns
347
+
348
+ config_table = Table(show_header=False, box=None, padding=(0, 1))
349
+ config_table.add_column("Item", style="cyan", no_wrap=True)
350
+ config_table.add_column("Status")
351
+
352
+ config_table.add_row("📁 Directory", status['ssh_dir_path'])
353
+ config_table.add_row(f"{'✅' if status['config_exists'] else '❌'} Config", str(status['config_exists']))
354
+ config_table.add_row(f"{'✅' if status['authorized_keys_exists'] else '❌'} Auth Keys", str(status['authorized_keys_exists']))
355
+ config_table.add_row(f"{'✅' if status['known_hosts_exists'] else '❌'} Known Hosts", str(status['known_hosts_exists']))
356
+
357
+ config_panel = Panel(config_table, title="SSH Config", border_style="yellow", padding=(1, 2), expand=False)
358
+
359
+ if status["keys"]:
360
+ keys_table = Table(show_header=True, box=None, padding=(0, 1), show_lines=False, expand=False)
361
+ keys_table.add_column("Key Name", style="bold cyan")
362
+ keys_table.add_column("Pub", justify="center")
363
+ keys_table.add_column("Priv", justify="center")
364
+
365
+ for key in status["keys"]:
366
+ pub_status = "✅" if key["public_exists"] else "❌"
367
+ priv_status = "✅" if key["private_exists"] else "❌"
368
+ keys_table.add_row(key["name"], pub_status, priv_status)
369
+
370
+ keys_panel = Panel(keys_table, title=f"SSH Keys ({len(status['keys'])})", border_style="yellow", padding=(1, 2), expand=False)
371
+
372
+ console.print(Columns([config_panel, keys_panel], equal=False, expand=True))
373
+ else:
374
+ console.print(config_panel)
375
+
376
+
377
+ def _display_config_files_status(status: dict[str, Any]) -> None:
378
+ """Display configuration files status."""
379
+ console.rule("[bold bright_blue]⚙️ Configuration Files[/bold bright_blue]")
380
+
381
+ if "error" in status:
382
+ console.print(Panel(f"❌ Error reading configuration: {status['error']}", title="Configuration Files", border_style="red", padding=(1, 2), expand=False))
383
+ return
384
+
385
+ public_percentage = (status["public_linked"] / status["public_count"] * 100) if status["public_count"] > 0 else 0
386
+ private_percentage = (status["private_linked"] / status["private_count"] * 100) if status["private_count"] > 0 else 0
387
+
388
+ table = Table(show_header=True, box=None, padding=(0, 2), expand=False)
389
+ table.add_column("Type", style="cyan", no_wrap=True)
390
+ table.add_column("Linked", justify="right")
391
+ table.add_column("Total", justify="right")
392
+ table.add_column("Progress", justify="right")
393
+
394
+ table.add_row("📂 Public", str(status['public_linked']), str(status['public_count']), f"{public_percentage:.0f}%")
395
+ table.add_row("🔒 Private", str(status['private_linked']), str(status['private_count']), f"{private_percentage:.0f}%")
396
+
397
+ overall_linked = status["public_linked"] + status["private_linked"]
398
+ overall_total = status["public_count"] + status["private_count"]
399
+ overall_percentage = (overall_linked / overall_total * 100) if overall_total > 0 else 0
400
+
401
+ border_style = "green" if overall_percentage > 80 else ("yellow" if overall_percentage > 50 else "red")
402
+
403
+ console.print(Panel(table, title=f"Configuration Files ({overall_percentage:.0f}% configured)", border_style=border_style, padding=(1, 2), expand=False))
404
+
405
+
406
+ def _display_tools_status(grouped_tools: dict[str, dict[str, bool]]) -> None:
407
+ """Display important tools installation status organized by groups."""
408
+ console.rule("[bold bright_magenta]🛠️ Important Tools[/bold bright_magenta]")
409
+
410
+ from rich.columns import Columns
411
+
412
+ all_group_panels = []
413
+ total_installed = 0
414
+ total_tools = 0
415
+
416
+ for group_name, tools in grouped_tools.items():
417
+ sorted_tools = sorted(tools.keys())
418
+ installed = [tool for tool, status in tools.items() if status]
419
+ total_installed += len(installed)
420
+ total_tools += len(tools)
421
+
422
+ num_columns = 8
423
+ tools_per_column = (len(sorted_tools) + num_columns - 1) // num_columns
424
+
425
+ tables = []
426
+ for col_idx in range(num_columns):
427
+ table = Table(show_header=False, box=None, padding=(0, 0), collapse_padding=True)
428
+ table.add_column("Tool", style="cyan", no_wrap=True, width=None)
429
+ table.add_column("", justify="center", width=2, no_wrap=True)
430
+
431
+ start_idx = col_idx * tools_per_column
432
+ end_idx = min(start_idx + tools_per_column, len(sorted_tools))
433
+
434
+ for i in range(start_idx, end_idx):
435
+ tool = sorted_tools[i]
436
+ status_icon = "✅" if tools[tool] else "❌"
437
+ table.add_row(tool, status_icon)
438
+
439
+ if start_idx < len(sorted_tools):
440
+ tables.append(table)
441
+
442
+ installed_percentage = (len(installed) / len(tools) * 100) if tools else 0
443
+ border_style = "green" if installed_percentage > 80 else ("yellow" if installed_percentage > 50 else "red")
444
+
445
+ group_display_name = group_name.replace("_", " ").title()
446
+ group_panel = Panel(
447
+ Columns(tables, equal=False, expand=False, padding=(0, 1)),
448
+ title=f"{group_display_name} ({len(installed)}/{len(tools)})",
449
+ border_style=border_style,
450
+ padding=(0, 1),
451
+ expand=False
452
+ )
453
+ all_group_panels.append(group_panel)
454
+
455
+ overall_percentage = (total_installed / total_tools * 100) if total_tools else 0
456
+ master_border_style = "green" if overall_percentage > 80 else ("yellow" if overall_percentage > 50 else "red")
457
+
458
+ from rich.console import Group
459
+ master_panel = Panel(
460
+ Group(*all_group_panels),
461
+ title=f"🛠️ Tools Overview ({total_installed}/{total_tools} installed - {overall_percentage:.0f}%)",
462
+ border_style=master_border_style,
463
+ padding=(1, 2),
464
+ expand=False
465
+ )
466
+ console.print(master_panel)
467
+
468
+
469
+ def _display_backup_status(status: dict[str, Any]) -> None:
470
+ """Display backup configuration status."""
471
+ console.rule("[bold bright_cyan]💾 Backup Configuration[/bold bright_cyan]")
472
+
473
+ table = Table(show_header=False, box=None, padding=(0, 1), expand=False)
474
+ table.add_column("Property", style="cyan", no_wrap=True)
475
+ table.add_column("Value", style="white")
476
+
477
+ table.add_row("🌥️ Cloud Config", status['cloud_config'])
478
+ table.add_row("📦 Backup Items", str(status['backup_items_count']))
479
+
480
+ border_style = "green" if status["cloud_config"] != "Not configured" else "yellow"
481
+
482
+ console.print(Panel(table, title="Backup Configuration", border_style=border_style, padding=(1, 2), expand=False))
483
+
484
+
485
+ def main() -> None:
486
+ """Main function to display comprehensive machine status."""
487
+ console.print("\n")
488
+ console.print(Panel(Text("📊 Machine Status Report", justify="center", style="bold white"), style="bold blue", padding=(1, 2)))
489
+ console.print("\n")
490
+
491
+ system_info = _check_system_info()
492
+ _display_system_info(system_info)
493
+
494
+ shell_status = _check_shell_profile_status()
495
+ _display_shell_status(shell_status)
496
+
497
+ machineconfig_repo = _check_machineconfig_repo()
498
+ _display_machineconfig_repo(machineconfig_repo)
499
+
500
+ repos_status = _check_repos_status()
501
+ _display_repos_status(repos_status)
502
+
503
+ ssh_status = _check_ssh_status()
504
+ _display_ssh_status(ssh_status)
505
+
506
+ config_status = _check_config_files_status()
507
+ _display_config_files_status(config_status)
508
+
509
+ tools_status = _check_important_tools()
510
+ _display_tools_status(tools_status)
511
+
512
+ backup_status = _check_backup_config()
513
+ _display_backup_status(backup_status)
514
+
515
+ console.print("\n")
516
+ console.print(Panel(Text("✨ Status report complete!", justify="center", style="bold green"), style="green", padding=(1, 2)))
517
+ console.print("\n")
518
+
519
+
520
+ if __name__ == "__main__":
521
+ main()
@@ -148,7 +148,7 @@ def _display_summary(results: list[RepositoryUpdateResult]) -> None:
148
148
  def main(verbose: bool = True, allow_password_prompt: bool = False) -> None:
149
149
  """Main function to update all configured repositories."""
150
150
  _ = verbose
151
- repos: list[Path] = [Path.home() / "code/machineconfig", Path.home() / "code/crocodile"]
151
+ repos: list[Path] = [Path.home() / "code/machineconfig"]
152
152
  try:
153
153
  tmp = read_ini(DEFAULTS_PATH)["general"]["repos"].split(",")
154
154
  if tmp[-1] == "":
@@ -42,6 +42,9 @@ def prep_agent_launch(agents_dir: Path, prompts_material: list[str], prompt_pref
42
42
  cmd_prefix = f"""
43
43
  #!/usr/bin/env bash
44
44
 
45
+ sleep 5
46
+ timeout 3 copilot --banner
47
+
45
48
  # AGENT-{idx}-LAUNCH-SCRIPT
46
49
  # Auto-generated by fire_agents.py
47
50
 
@@ -74,15 +77,17 @@ sleep 0.1
74
77
  api_key = api_keys[idx % len(api_keys)] if api_keys else ""
75
78
  # Export the environment variable so it's available to subshells
76
79
  cmd = f"""
80
+
77
81
  export GEMINI_API_KEY={shlex.quote(api_key)}
78
82
  echo "Using Gemini API key $GEMINI_API_KEY"
83
+
79
84
  gemini {model_arg} --yolo --prompt {safe_path}
80
85
  """
81
86
  case "cursor-agent":
82
87
  # As originally implemented
83
88
  cmd = f"""
84
89
 
85
- cursor-agent --print --output-format text < {prompt_path}
90
+ cursor-agent --print --output-format text {prompt_path}
86
91
 
87
92
  """
88
93
  case "crush":
@@ -24,7 +24,6 @@ class FireJobArgs:
24
24
  PathExport: bool = False
25
25
  git_pull: bool = False
26
26
  optimized: bool = False
27
- Nprocess: int = 1
28
27
  zellij_tab: Optional[str] = None
29
28
  watch: bool = False
30
29
 
@@ -96,6 +95,10 @@ def _convert_value_type(value: str) -> object:
96
95
  elif value.lower() in ('false', '0', 'no', 'off'):
97
96
  return False
98
97
 
98
+ # Try to convert None
99
+ if value.lower() == 'none':
100
+ return None
101
+
99
102
  # Try to parse as list (comma-separated values)
100
103
  if ',' in value:
101
104
  items = [_convert_value_type(item.strip()) for item in value.split(',')]
@@ -1,11 +1,7 @@
1
1
  from machineconfig.utils.path_extended import PathExtended
2
- from machineconfig.utils.terminal import Response
3
2
  from machineconfig.scripts.python.get_zellij_cmd import get_zellij_cmd
4
- from machineconfig.utils.source_of_truth import CONFIG_PATH, DEFAULTS_PATH
5
- from machineconfig.utils.io import read_ini
6
3
  from machineconfig.utils.code import write_shell_script_to_file
7
4
  import platform
8
- import subprocess
9
5
  from rich.console import Console
10
6
  from rich.panel import Panel
11
7
 
@@ -33,9 +29,6 @@ def delete_remote_repo_copy_and_push_local(remote_repo: str, local_repo: str, cl
33
29
  console.print(Panel("✅ Repository successfully pushed to cloud", title="[bold green]Repo Sync[/bold green]", border_style="green"))
34
30
 
35
31
 
36
- # import sys
37
- # import subprocess
38
-
39
32
 
40
33
  def get_wt_cmd(wd1: PathExtended, wd2: PathExtended) -> str:
41
34
  lines = [
@@ -60,42 +53,6 @@ def inspect_repos(repo_local_root: str, repo_remote_root: str):
60
53
  raise NotImplementedError(f"Platform {platform.system()} not implemented.")
61
54
 
62
55
 
63
- def fetch_dotfiles():
64
- console.print(Panel("📁 Fetching Dotfiles", title="[bold blue]Dotfiles[/bold blue]", border_style="blue"))
65
-
66
- cloud_resolved = read_ini(DEFAULTS_PATH)["general"]["rclone_config_name"]
67
- console.print(Panel(f"⚠️ Using default cloud: `{cloud_resolved}` from {DEFAULTS_PATH}", width=150, border_style="yellow"))
68
-
69
- dotfiles_local = PathExtended.home().joinpath("dotfiles")
70
- CONFIG_PATH.joinpath("remote").mkdir(parents=True, exist_ok=True)
71
- dotfiles_remote = PathExtended(CONFIG_PATH).joinpath("remote", dotfiles_local.rel2home())
72
- remote_path = dotfiles_local.get_remote_path(rel2home=True, os_specific=False, root="myhome") + ".zip.enc"
73
-
74
- console.print(Panel("📥 Downloading dotfiles from cloud...", width=150, border_style="blue"))
75
-
76
- dotfiles_remote.from_cloud(remotepath=remote_path, cloud=cloud_resolved, unzip=True, decrypt=True, rel2home=True, os_specific=False, pwd=None)
77
-
78
- console.print(Panel("🗑️ Removing old dotfiles and replacing with cloud version...", width=150, border_style="blue"))
79
-
80
- dotfiles_local.delete(sure=True)
81
- dotfiles_remote.move(folder=PathExtended.home())
82
- script = f"""
83
- # rm -rf {dotfiles_local}
84
- # mv {dotfiles_remote} {dotfiles_local}
85
- """
86
- if platform.system() == "Linux":
87
- script += """
88
- sudo chmod 600 $HOME/.ssh/*
89
- sudo chmod 700 $HOME/.ssh
90
- sudo chmod +x $HOME/dotfiles/scripts/linux -R
91
- """
92
- shell_path = write_shell_script_to_file(shell_script=script)
93
- completed = subprocess.run(f". {shell_path}", capture_output=True, check=False, text=True, shell=True)
94
- Response.from_completed_process(completed).capture().print()
95
-
96
- console.print(Panel("✅ Dotfiles successfully fetched and installed", title="[bold green]Dotfiles[/bold green]", border_style="green"))
97
-
98
-
99
56
  def check_dotfiles_version_is_beyond(commit_dtm: str, update: bool) -> bool:
100
57
  dotfiles_path = str(PathExtended.home().joinpath("dotfiles"))
101
58
  from git import Repo
@@ -6,7 +6,6 @@
6
6
  # # or use ots executable to do this job
7
7
 
8
8
  # import requests
9
- # import crocodile.file_management as fm
10
9
  # import base64
11
10
  # from typing import Optional
12
11
 
@@ -4,12 +4,6 @@ $Path = $HOME + '\data\share_smb'
4
4
 
5
5
  Get-SmbShare
6
6
 
7
-
8
- #. activate_ve
9
- #python -m machineconfig.scripts.python.share_smb
10
- #. $HOME/tmp_results/shells/python_return_command.ps1
11
-
12
- # make $path dir if it does not exist
13
7
  if (!(Test-Path -Path $Path)) {
14
8
  New-Item -ItemType Directory -Path $Path
15
9
  }