machineconfig 5.67__py3-none-any.whl → 5.69__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 (31) hide show
  1. machineconfig/jobs/installer/installer_data.json +3 -3
  2. machineconfig/scripts/python/agents.py +29 -24
  3. machineconfig/scripts/python/ai/generate_files.py +4 -4
  4. machineconfig/scripts/python/ai/solutions/generic.py +1 -0
  5. machineconfig/scripts/python/croshell.py +1 -1
  6. machineconfig/scripts/python/devops.py +13 -7
  7. machineconfig/scripts/python/devops_helpers/cli_config.py +26 -14
  8. machineconfig/scripts/python/devops_helpers/cli_data.py +1 -1
  9. machineconfig/scripts/python/devops_helpers/cli_nw.py +14 -7
  10. machineconfig/scripts/python/devops_helpers/cli_repos.py +36 -18
  11. machineconfig/scripts/python/devops_helpers/cli_self.py +19 -11
  12. machineconfig/scripts/python/ftpx.py +6 -6
  13. machineconfig/scripts/python/interactive.py +3 -3
  14. machineconfig/scripts/python/nw/mount_nfs +1 -1
  15. machineconfig/scripts/python/nw/mount_nfs.py +3 -3
  16. machineconfig/scripts/python/nw/mount_ssh.py +3 -3
  17. machineconfig/scripts/python/nw/wsl_windows_transfer.py +2 -2
  18. machineconfig/scripts/python/repos_helpers/count_lines_frontend.py +1 -1
  19. machineconfig/scripts/python/sessions.py +9 -4
  20. machineconfig/scripts/windows/mounts/mount_ssh.ps1 +1 -1
  21. machineconfig/setup_linux/apps.sh +0 -1
  22. machineconfig/setup_linux/web_shortcuts/interactive.sh +7 -7
  23. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +7 -7
  24. machineconfig/utils/installer_utils/installer.py +9 -8
  25. machineconfig/utils/ssh.py +466 -247
  26. machineconfig/utils/ssh_utils/utils.py +0 -0
  27. {machineconfig-5.67.dist-info → machineconfig-5.69.dist-info}/METADATA +1 -1
  28. {machineconfig-5.67.dist-info → machineconfig-5.69.dist-info}/RECORD +31 -30
  29. {machineconfig-5.67.dist-info → machineconfig-5.69.dist-info}/WHEEL +0 -0
  30. {machineconfig-5.67.dist-info → machineconfig-5.69.dist-info}/entry_points.txt +0 -0
  31. {machineconfig-5.67.dist-info → machineconfig-5.69.dist-info}/top_level.txt +0 -0
@@ -8,13 +8,13 @@
8
8
  "fileNamePattern": {
9
9
  "amd64": {
10
10
  "linux": "jq-linux-amd64",
11
- "windows": null,
12
- "macos": null
11
+ "windows": "jq-windows-amd64.exe",
12
+ "macos": "jq-macos-amd64"
13
13
  },
14
14
  "arm64": {
15
15
  "linux": "jq-linux-arm64",
16
16
  "windows": null,
17
- "macos": null
17
+ "macos": "jq-macos-arm64"
18
18
  }
19
19
  }
20
20
  },
@@ -9,19 +9,19 @@ from machineconfig.scripts.python.helpers_fire.fire_agents_helper_types import A
9
9
 
10
10
 
11
11
  def create(
12
- agent: AGENTS = typer.Option(default=..., help=f"Agent type. One of {', '.join(get_args(AGENTS)[:3])}"),
13
- machine: MATCHINE = typer.Option(default=..., help=f"Machine to run agents on. One of {', '.join(get_args(MATCHINE))}"),
14
- model: MODEL = typer.Option(default=..., help=f"Model to use (for crush agent). One of {', '.join(get_args(MODEL)[:3])}"),
15
- provider: PROVIDER = typer.Option(default=..., help=f"Provider to use (for crush agent). One of {', '.join(get_args(PROVIDER)[:3])}"),
16
- context_path: Optional[Path] = typer.Option(None, help="Path to the context file/folder, defaults to .ai/todo/"),
17
- separator: str = typer.Option("\n", help="Separator for context"),
18
- tasks_per_prompt: int = typer.Option(13, help="Number of tasks per prompt"),
19
- prompt: Optional[str] = typer.Option(None, help="Prompt prefix as string"),
20
- prompt_path: Optional[Path] = typer.Option(None, help="Path to prompt file"),
21
- job_name: str = typer.Option("AI_Agents", help="Job name"),
22
- separate_prompt_from_context: bool = typer.Option(True, help="Keep prompt material in separate file to the context."),
23
- output_path: Optional[Path] = typer.Option(None, help="Path to write the layout.json file"),
24
- agents_dir: Optional[Path] = typer.Option(None, help="Directory to store agent files. If not provided, will be constructed automatically."),
12
+ agent: AGENTS = typer.Option(..., "--agents", "-a", help=f"Agent type. One of {', '.join(get_args(AGENTS)[:3])}"),
13
+ machine: MATCHINE = typer.Option(..., "--machine", "-m", help=f"Machine to run agents on. One of {', '.join(get_args(MATCHINE))}"),
14
+ model: MODEL = typer.Option(..., "--model", "-M", help=f"Model to use (for crush agent). One of {', '.join(get_args(MODEL)[:3])}"),
15
+ provider: PROVIDER = typer.Option(..., "--provider", "-p", help=f"Provider to use (for crush agent). One of {', '.join(get_args(PROVIDER)[:3])}"),
16
+ context_path: Optional[Path] = typer.Option(None, "--context-path", "-c", help="Path to the context file/folder, defaults to .ai/todo/"),
17
+ separator: str = typer.Option("\n", "--separator", "-s", help="Separator for context"),
18
+ agent_load: int = typer.Option(13, "--agent-load", "-al", help="Number of tasks per prompt"),
19
+ prompt: Optional[str] = typer.Option(None, "--prompt", "-p", help="Prompt prefix as string"),
20
+ prompt_path: Optional[Path] = typer.Option(None, "--prompt-path", "-pp", help="Path to prompt file"),
21
+ job_name: str = typer.Option("AI_Agents", "--job-name", "-j", help="Job name"),
22
+ separate: bool = typer.Option(True, "--separate", "-sep", help="Keep prompt material in separate file to the context."),
23
+ output_path: Optional[Path] = typer.Option(None, "--output-path", "-o", help="Path to write the layout.json file"),
24
+ agents_dir: Optional[Path] = typer.Option(None, "--agents-dir", "-ad", help="Directory to store agent files. If not provided, will be constructed automatically."),
25
25
  ):
26
26
 
27
27
  from machineconfig.scripts.python.helpers_fire.fire_agents_help_launch import prep_agent_launch, get_agents_launch_layout
@@ -49,7 +49,7 @@ def create(
49
49
  raise typer.BadParameter(f"Path does not exist: {context_path_resolved}")
50
50
 
51
51
  if context_path_resolved.is_file():
52
- prompt_material_re_splitted = chunk_prompts(context_path_resolved, tasks_per_prompt=tasks_per_prompt, joiner=separator)
52
+ prompt_material_re_splitted = chunk_prompts(context_path_resolved, tasks_per_prompt=agent_load, joiner=separator)
53
53
  elif context_path_resolved.is_dir():
54
54
  files = [f for f in context_path_resolved.rglob("*") if f.is_file()]
55
55
  if not files:
@@ -64,14 +64,13 @@ def create(
64
64
  else:
65
65
  prompt_prefix = cast(str, prompt)
66
66
  agent_selected = agent
67
- keep_material_in_separate_file_input = separate_prompt_from_context
68
67
  if agents_dir is None: agents_dir = repo_root / ".ai" / f"tmp_prompts/{job_name}_{randstr()}"
69
68
  else:
70
69
  import shutil
71
70
  if agents_dir.exists():
72
71
  shutil.rmtree(agents_dir)
73
72
  prep_agent_launch(repo_root=repo_root, agents_dir=agents_dir, prompts_material=prompt_material_re_splitted,
74
- keep_material_in_separate_file=keep_material_in_separate_file_input,
73
+ keep_material_in_separate_file=separate,
75
74
  prompt_prefix=prompt_prefix, machine=machine, agent=agent_selected, model=model, provider=provider,
76
75
  job_name=job_name)
77
76
  layoutfile = get_agents_launch_layout(session_root=agents_dir)
@@ -82,9 +81,9 @@ agents create "{context_path_resolved}" \\
82
81
  --agent "{agent_selected}" \\
83
82
  --machine "{machine}" \\
84
83
  --job-name "{job_name}" \\
85
- --tasks-per-prompt {tasks_per_prompt} \\
84
+ --agent_load {agent_load} \\
86
85
  --separator "{separator}" \\
87
- {"--separate-prompt-from-context" if keep_material_in_separate_file_input else ""}
86
+ {"--separate" if separate else ""}
88
87
  """
89
88
  (agents_dir / "aa_agents_relaunch.py").write_text(data=regenerate_py_code, encoding="utf-8")
90
89
  layout_output_path = output_path if output_path is not None else agents_dir / "layout.json"
@@ -164,7 +163,7 @@ def get_app():
164
163
  agents_app = typer.Typer(help="🤖 AI Agents management subcommands", no_args_is_help=True)
165
164
  sep = "\n"
166
165
  agents_full_help = f"""
167
- Create agents layout file, ready to run.
166
+ [cr] Create agents layout file, ready to run.
168
167
  {sep}
169
168
  PROVIDER options: {', '.join(get_args(PROVIDER))}
170
169
  {sep}
@@ -173,13 +172,19 @@ AGENT options: {', '.join(get_args(AGENTS))}
173
172
  MODEL options: {sep.join(get_args(MODEL))}
174
173
  """
175
174
  agents_app.command("create", no_args_is_help=True, help=agents_full_help)(create)
176
- agents_app.command("collect", no_args_is_help=True, help="Collect all agent materials into a single file.")(collect)
177
- agents_app.command("make-template", no_args_is_help=False, help="Create a template for fire agents")(template)
178
- agents_app.command("make-config", no_args_is_help=False, help="Initialize AI configurations in the current repository")(init_config)
175
+ agents_app.command("cr", no_args_is_help=True, help="Create agents layout file, ready to run.", hidden=True)(create)
176
+ agents_app.command("collect", no_args_is_help=True, help="[cl] Collect all agent materials into a single file.")(collect)
177
+ agents_app.command("c", no_args_is_help=True, help="Collect all agent materials into a single file.", hidden=True)(collect)
178
+ agents_app.command("make-template", no_args_is_help=False, help="[mt] Create a template for fire agents")(template)
179
+ agents_app.command("mt", no_args_is_help=False, help="Create a template for fire agents", hidden=True)(template)
180
+ agents_app.command("make-config", no_args_is_help=False, help="[mc] Initialize AI configurations in the current repository")(init_config)
181
+ agents_app.command("mc", no_args_is_help=False, help="Initialize AI configurations in the current repository", hidden=True)(init_config)
179
182
  from machineconfig.scripts.python.ai.generate_files import main
180
- agents_app.command("make-todo", no_args_is_help=True, help="Generate a markdown file listing all Python files in the repo")(main)
183
+ agents_app.command("make-todo", no_args_is_help=True, help="[mt] Generate a markdown file listing all Python files in the repo")(main)
184
+ agents_app.command("mt", no_args_is_help=True, help="Generate a markdown file listing all Python files in the repo", hidden=True)(main)
181
185
  from machineconfig.scripts.python.ai.generate_files import create_symlink_command
182
- agents_app.command(name="make-symlinks", no_args_is_help=True, help="Create symlinks to the current repo in ~/code_copies/")(create_symlink_command)
186
+ agents_app.command(name="make-symlinks", no_args_is_help=True, help="[ms] Create symlinks to the current repo in ~/code_copies/")(create_symlink_command)
187
+ agents_app.command(name="ms", no_args_is_help=True, help="Create symlinks to the current repo in ~/code_copies/", hidden=True)(create_symlink_command)
183
188
  return agents_app
184
189
 
185
190
 
@@ -12,7 +12,7 @@ import shutil
12
12
 
13
13
  def get_python_files(repo_root: Path, exclude_init: bool = False) -> list[str]:
14
14
  """Get all Python files relative to repo root."""
15
- excluded_parts = {".venv", "__pycache__", ".git", "build", "dist"}
15
+ excluded_parts = {".venv", "__pycache__", ".git", "build", "dist", ".ai"}
16
16
  excluded_patterns = {"*.egg-info"}
17
17
 
18
18
  # Get all .py files recursively
@@ -251,12 +251,12 @@ def main(
251
251
  pattern: Annotated[str, typer.Argument(help="Pattern or keyword to match files by")],
252
252
  repo: Annotated[str, typer.Argument(help="Repository path. Can be any directory within a git repository.")] = str(Path.cwd()),
253
253
  strategy: Annotated[Literal["name", "keywords"], typer.Option("-s", "--strategy", help="Strategy to filter files: 'name' for filename matching, 'keywords' for content matching")] = "name",
254
- exclude_init: Annotated[bool, typer.Option("-e", "--exclude-init", help="Exclude __init__.py files from the checklist")] = False,
254
+ exclude_init: Annotated[bool, typer.Option("-x", "--exclude-init", help="Exclude __init__.py files from the checklist")] = True,
255
255
  include_line_count: Annotated[bool, typer.Option("-l", "--line-count", help="Include line count column in the output")] = False,
256
256
  output_path: Annotated[str, typer.Option("-o", "--output-path", help="Base path for output files relative to repo root")] = ".ai/todo/files",
257
257
  format_type: Annotated[Literal["csv", "md", "txt"], typer.Option("-f", "--format", help="Output format: csv, md (markdown), or txt")] = "md",
258
- split_every: Annotated[Optional[int], typer.Option("--split-every", help="Split output into multiple files, each containing at most this many results")] = None,
259
- split_to: Annotated[Optional[int], typer.Option("--split-to", help="Split output into exactly this many files")] = None,
258
+ split_every: Annotated[Optional[int], typer.Option("--split-every", "-e", help="Split output into multiple files, each containing at most this many results")] = None,
259
+ split_to: Annotated[Optional[int], typer.Option("--split-to", "-t", help="Split output into exactly this many files")] = None,
260
260
  ) -> None:
261
261
  """Generate checklist with Python and shell script files in the repository filtered by pattern."""
262
262
  repo_path = Path(repo).expanduser().absolute()
@@ -48,6 +48,7 @@ def adjust_gitignore(repo_root: Path) -> None:
48
48
  "CLAUDE.md",
49
49
  "CRUSH.md",
50
50
  ".cursor",
51
+ ".clinerules"
51
52
  ".github/instructions",
52
53
  ".github/chatmodes",
53
54
  ".github/prompts",
@@ -150,7 +150,7 @@ from pathlib import Path
150
150
  else:
151
151
  console.print(Panel("❌ Could not determine the local machineconfig repo root. Please ensure the `REPO_ROOT` in `source_of_truth.py` is correctly set to the local path of the machineconfig repo, or do not use the `--local` flag.", title="Error", border_style="red"))
152
152
  return
153
- else: ve_line = "--with machineconfig[plot]>=5.65"
153
+ else: ve_line = "--with machineconfig[plot]>=5.67"
154
154
  fire_line = f"uv run --python 3.14 {ve_line} {interpreter} {interactivity} {profile} {str(pyfile)}"
155
155
 
156
156
  from machineconfig.utils.code import run_shell_script
@@ -12,20 +12,26 @@ import machineconfig.scripts.python.devops_helpers.cli_nw as cli_network
12
12
 
13
13
  def get_app():
14
14
  app = typer.Typer(help="🛠️ DevOps operations", no_args_is_help=True, add_completion=True)
15
- @app.command(no_args_is_help=True)
16
- def install( which: Optional[str] = typer.Option(None, "--which", "-w", help="Comma-separated list of program names to install."),
17
- group: Optional[str] = typer.Option(None, "--group", "-g", help="Groups names. A group is bundle of apps. See available groups when running interactively."),
15
+ def install(which: Optional[str] = typer.Argument(None, help="Comma-separated list of program names to install, or group name if --group flag is set."),
16
+ group: bool = typer.Option(False, "--group", "-g", help="Treat 'which' as a group name. A group is bundle of apps."),
18
17
  interactive: bool = typer.Option(False, "--interactive", "-ia", help="Interactive selection of programs to install."),
19
18
  ) -> None:
20
19
  """📦 Install essential packages"""
21
20
  import machineconfig.utils.installer_utils.installer as installer_entry_point
22
21
  installer_entry_point.main(which=which, group=group, interactive=interactive)
23
22
  _ = install
24
- app.add_typer(cli_repos.app, name="repos")
25
- app.add_typer(cli_config.config_apps, name="config")
23
+ app.command("install", no_args_is_help=True, help="🛠️ [i] Install essential packages")(install)
24
+ app.command("i", no_args_is_help=True, help="Install essential packages", hidden=True)(install)
25
+ app.add_typer(cli_repos.get_app(), name="repos")
26
+ app.add_typer(cli_repos.get_app(), name="r", hidden=True)
27
+ app.add_typer(cli_config.get_app(), name="config")
28
+ app.add_typer(cli_config.get_app(), name="c", hidden=True)
26
29
  app.add_typer(cli_data.app_data, name="data")
27
- app.add_typer(cli_self.cli_app, name="self")
28
- app.add_typer(cli_network.nw_apps, name="network")
30
+ app.add_typer(cli_data.app_data, name="d", hidden=True)
31
+ app.add_typer(cli_self.get_app(), name="self")
32
+ app.add_typer(cli_self.get_app(), name="s", hidden=True)
33
+ app.add_typer(cli_network.get_app(), name="network")
34
+ app.add_typer(cli_network.get_app(), name="n", hidden=True)
29
35
  return app
30
36
 
31
37
  def main():
@@ -4,10 +4,8 @@ from typing import Literal, Annotated, Optional
4
4
  from pathlib import Path
5
5
  import typer
6
6
 
7
- config_apps = typer.Typer(help="⚙️ Configuration subcommands", no_args_is_help=True)
8
7
 
9
8
 
10
- @config_apps.command(no_args_is_help=True)
11
9
  def private(method: Literal["symlink", "copy"] = typer.Option(..., "--method", "-m", help="Method to use for linking files"),
12
10
  on_conflict: Literal["throwError", "overwriteSelfManaged", "backupSelfManaged", "overwriteDefaultPath", "backupDefaultPath"] = typer.Option("throwError", "--on-conflict", "-o", help="Action to take on conflict"),
13
11
  which: Optional[str] = typer.Option(None, "--which", "-w", help="Specific items to process"),
@@ -16,7 +14,6 @@ def private(method: Literal["symlink", "copy"] = typer.Option(..., "--method", "
16
14
  import machineconfig.profile.create_links_export as create_links_export
17
15
  create_links_export.main_private_from_parser(method=method, on_conflict=on_conflict, which=which, interactive=interactive)
18
16
 
19
- @config_apps.command(no_args_is_help=True)
20
17
  def public(method: Literal["symlink", "copy"] = typer.Option(..., "--method", "-m", help="Method to use for setting up the config file."),
21
18
  on_conflict: Literal["throwError", "overwriteDefaultPath", "backupDefaultPath"] = typer.Option("throwError", "--on-conflict", "-o", help="Action to take on conflict"),
22
19
  which: Optional[str] = typer.Option(None, "--which", "-w", help="Specific items to process"),
@@ -25,7 +22,6 @@ def public(method: Literal["symlink", "copy"] = typer.Option(..., "--method", "-
25
22
  import machineconfig.profile.create_links_export as create_links_export
26
23
  create_links_export.main_public_from_parser(method=method, on_conflict=on_conflict, which=which, interactive=interactive)
27
24
 
28
- @config_apps.command(no_args_is_help=True)
29
25
  def dotfile(file: Annotated[str, typer.Argument(help="file/folder path.")],
30
26
  overwrite: Annotated[bool, typer.Option("--overwrite", "-o", help="Overwrite.")] = False,
31
27
  dest: Annotated[str, typer.Option("--dest", "-d", help="destination folder")] = "",
@@ -35,22 +31,19 @@ def dotfile(file: Annotated[str, typer.Argument(help="file/folder path.")],
35
31
  dotfile_module.main(file=file, overwrite=overwrite, dest=dest)
36
32
 
37
33
 
38
- @config_apps.command(no_args_is_help=False)
39
34
  def shell():
40
35
  """🔗 Configure your shell profile."""
41
36
  from machineconfig.profile.create_shell_profile import create_default_shell_profile
42
37
  create_default_shell_profile()
43
38
 
44
- @config_apps.command(no_args_is_help=False)
45
39
  def path():
46
40
  """📚 NAVIGATE PATH variable with TUI"""
47
41
  from machineconfig.scripts.python import env_manager as navigator
48
42
  from pathlib import Path
49
43
  path = Path(navigator.__file__).resolve().parent.joinpath("path_manager_tui.py")
50
44
  from machineconfig.utils.code import run_shell_script
51
- run_shell_script(f"uv run --with machineconfig>=5.65,textual {path}")
45
+ run_shell_script(f"uv run --with machineconfig>=5.67,textual {path}")
52
46
 
53
- @config_apps.command(no_args_is_help=False)
54
47
  def pwsh_theme():
55
48
  """🔗 Select powershell prompt theme."""
56
49
  import machineconfig.scripts.python.devops_helpers.themes as themes
@@ -58,12 +51,31 @@ def pwsh_theme():
58
51
  import subprocess
59
52
  subprocess.run(["pwsh", "-File", str(file)])
60
53
 
61
- @config_apps.command(no_args_is_help=False)
62
54
  def copy_assets(which: Literal["scripts", "settings", "both"] = typer.Option(..., "--which", "-w", help="Which assets to copy")):
63
55
  """🔗 Copy asset files from library to machine."""
64
56
  import machineconfig.profile.create_helper as create_helper
65
- if which == "both":
66
- create_helper.copy_assets_to_machine(which="scripts")
67
- create_helper.copy_assets_to_machine(which="settings")
68
- else:
69
- create_helper.copy_assets_to_machine(which=which)
57
+ match which:
58
+ case "both":
59
+ create_helper.copy_assets_to_machine(which="scripts")
60
+ create_helper.copy_assets_to_machine(which="settings")
61
+ case _:
62
+ create_helper.copy_assets_to_machine(which=which)
63
+
64
+
65
+ def get_app():
66
+ config_apps = typer.Typer(help="⚙️ [c] configuration subcommands", no_args_is_help=True)
67
+ config_apps.command("private", no_args_is_help=True, help="🔗 [p] Manage private configuration files.")(private)
68
+ config_apps.command("p", no_args_is_help=True, help="Manage private configuration files.", hidden=True)(private)
69
+ config_apps.command("public", no_args_is_help=True, help="🔗 [u] Manage public configuration files.")(public)
70
+ config_apps.command("u", no_args_is_help=True, help="Manage public configuration files.", hidden=True)(public)
71
+ config_apps.command("dotfile", no_args_is_help=True, help="🔗 [d] Manage dotfiles.")(dotfile)
72
+ config_apps.command("d", no_args_is_help=True, help="Manage dotfiles.", hidden=True)(dotfile)
73
+ config_apps.command("shell", no_args_is_help=True, help="🔗 [s] Configure your shell profile.")(shell)
74
+ config_apps.command("s", no_args_is_help=True, help="Configure your shell profile.", hidden=True)(shell)
75
+ config_apps.command("path", no_args_is_help=True, help="📚 [a] NAVIGATE PATH variable with TUI")(path)
76
+ config_apps.command("a", no_args_is_help=True, help="NAVIGATE PATH variable with TUI", hidden=True)(path)
77
+ config_apps.command("pwsh-theme", no_args_is_help=True, help="🔗 [t] Select powershell prompt theme.")(pwsh_theme)
78
+ config_apps.command("t", no_args_is_help=True, help="Select powershell prompt theme.", hidden=True)(pwsh_theme)
79
+ config_apps.command("copy-assets", no_args_is_help=True, help="🔗 [c] Copy asset files from library to machine.")
80
+ config_apps.command("c", no_args_is_help=True, help="Copy asset files from library to machine.", hidden=True)(copy_assets)
81
+ return config_apps
@@ -1,7 +1,7 @@
1
1
 
2
2
  import typer
3
3
 
4
- app_data = typer.Typer(help="💾 Data subcommands", no_args_is_help=True)
4
+ app_data = typer.Typer(help="💾 [d] data subcommands", no_args_is_help=True)
5
5
 
6
6
  @app_data.command()
7
7
  def backup():
@@ -4,13 +4,8 @@ import machineconfig.scripts.python.devops_helpers.cli_share_server as cli_share
4
4
  import typer
5
5
  from typing import Optional
6
6
 
7
- nw_apps = typer.Typer(help="🔐 Network subcommands", no_args_is_help=True)
8
7
 
9
8
 
10
- nw_apps.command(name="share-terminal", help="📡 Share terminal via web browser")(cli_terminal.main)
11
- nw_apps.command(name="share-server", help="🌐 Start local/global server to share files/folders via web browser", no_args_is_help=True)(cli_share_server.main)
12
-
13
- @nw_apps.command()
14
9
  def install_ssh_server():
15
10
  """📡 SSH install server"""
16
11
  import platform
@@ -23,7 +18,6 @@ def install_ssh_server():
23
18
  from machineconfig.utils.code import run_shell_script
24
19
  run_shell_script(script=SSH_SERVER.read_text(encoding="utf-8"))
25
20
 
26
- @nw_apps.command(no_args_is_help=True)
27
21
  def add_ssh_key(path: Optional[str] = typer.Option(None, help="Path to the public key file"),
28
22
  choose: bool = typer.Option(False, "--choose", "-c", help="Choose from available public keys in ~/.ssh/*.pub"),
29
23
  value: bool = typer.Option(False, "--value", "-v", help="Paste the public key content manually"),
@@ -32,8 +26,21 @@ def add_ssh_key(path: Optional[str] = typer.Option(None, help="Path to the publi
32
26
  """🔑 SSH add pub key to this machine so its accessible by owner of corresponding private key."""
33
27
  import machineconfig.scripts.python.devops_helpers.devops_add_ssh_key as helper
34
28
  helper.main(pub_path=path, pub_choose=choose, pub_val=value, from_github=github)
35
- @nw_apps.command()
36
29
  def add_ssh_identity():
37
30
  """🗝️ SSH add identity (private key) to this machine"""
38
31
  import machineconfig.scripts.python.devops_helpers.devops_add_identity as helper
39
32
  helper.main()
33
+
34
+ def get_app():
35
+ nw_apps = typer.Typer(help="🔐 [n] Network subcommands", no_args_is_help=True)
36
+ nw_apps.command(name="share-terminal", help="📡 [t] Share terminal via web browser")(cli_terminal.main)
37
+ nw_apps.command(name="t", help="Share terminal via web browser", hidden=True)(cli_terminal.main)
38
+ nw_apps.command(name="share-server", help="🌐 [s] Start local/global server to share files/folders via web browser", no_args_is_help=True)(cli_share_server.main)
39
+ nw_apps.command(name="s", help="Start local/global server to share files/folders via web browser", hidden=True, no_args_is_help=True)(cli_share_server.main)
40
+ nw_apps.command(name="install-ssh-server", help="📡 [i] Install SSH server")(install_ssh_server)
41
+ nw_apps.command(name="i", help="Install SSH server", hidden=True)(install_ssh_server)
42
+ nw_apps.command(name="add-ssh-key", help="🔑 [k] Add SSH public key to this machine")(add_ssh_key)
43
+ nw_apps.command(name="k", help="Add SSH public key to this machine", hidden=True)(add_ssh_key)
44
+ nw_apps.command(name="add-ssh-identity", help="🗝️ [a] Add SSH identity (private key) to this machine")(add_ssh_identity)
45
+ nw_apps.command(name="a", help="Add SSH identity (private key) to this machine", hidden=True)(add_ssh_identity)
46
+ return nw_apps
@@ -11,24 +11,18 @@ import typer
11
11
  from machineconfig.scripts.python.helpers_repos.secure_repo import main as secure_repo_main
12
12
 
13
13
 
14
- app = typer.Typer(help="📁 Manage development repositories", no_args_is_help=True)
15
- sync_app = typer.Typer(help="🔄 Manage repository specifications and syncing", no_args_is_help=True)
16
- app.add_typer(sync_app, name="mirror", help="🔄 mirror repositories using saved specs")
17
-
18
14
  DirectoryArgument = Annotated[Optional[str], typer.Argument(help="📁 Directory containing repo(s).")]
19
15
  RecursiveOption = Annotated[bool, typer.Option("--recursive", "-r", help="🔍 Recurse into nested repositories.")]
20
16
  NoSyncOption = Annotated[bool, typer.Option("--no-sync", help="🚫 Disable automatic uv sync after pulls.")]
21
17
  CloudOption = Annotated[Optional[str], typer.Option("--cloud", "-c", help="☁️ Upload to or download from this cloud remote.")]
22
18
 
23
19
 
24
- @app.command(no_args_is_help=True)
25
20
  def push(directory: DirectoryArgument = None, recursive: RecursiveOption = False, no_sync: NoSyncOption = False) -> None:
26
21
  """🚀 Push changes across repositories."""
27
22
  from machineconfig.scripts.python.repos_helpers.entrypoint import git_operations
28
23
  git_operations(directory, pull=False, commit=False, push=True, recursive=recursive, no_sync=no_sync)
29
24
 
30
25
 
31
- @app.command(no_args_is_help=True)
32
26
  def pull(directory: DirectoryArgument = None, recursive: RecursiveOption = False, no_sync: NoSyncOption = False) -> None:
33
27
  """⬇️ Pull changes across repositories."""
34
28
  from machineconfig.scripts.python.repos_helpers.entrypoint import git_operations
@@ -36,7 +30,6 @@ def pull(directory: DirectoryArgument = None, recursive: RecursiveOption = False
36
30
  git_operations(directory, pull=True, commit=False, push=False, recursive=recursive, no_sync=no_sync)
37
31
 
38
32
 
39
- @app.command(no_args_is_help=True)
40
33
  def commit(directory: DirectoryArgument = None, recursive: RecursiveOption = False, no_sync: NoSyncOption = False) -> None:
41
34
  """💾 Commit changes across repositories."""
42
35
  from machineconfig.scripts.python.repos_helpers.entrypoint import git_operations
@@ -44,14 +37,12 @@ def commit(directory: DirectoryArgument = None, recursive: RecursiveOption = Fal
44
37
  git_operations(directory, pull=False, commit=True, push=False, recursive=recursive, no_sync=no_sync)
45
38
 
46
39
 
47
- @app.command(no_args_is_help=True)
48
40
  def sync(directory: DirectoryArgument = None, recursive: RecursiveOption = False, no_sync: NoSyncOption = False) -> None:
49
41
  """🔄 Pull, commit, and push changes across repositories."""
50
42
  from machineconfig.scripts.python.repos_helpers.entrypoint import git_operations
51
43
  git_operations(directory, pull=True, commit=True, push=True, recursive=recursive, no_sync=no_sync)
52
44
 
53
45
 
54
- @sync_app.command(no_args_is_help=True)
55
46
  def capture(directory: DirectoryArgument = None, cloud: CloudOption = None) -> None:
56
47
  """📝 Record repositories into a repos.json specification."""
57
48
  from machineconfig.scripts.python.repos_helpers.entrypoint import resolve_directory
@@ -65,7 +56,6 @@ def capture(directory: DirectoryArgument = None, cloud: CloudOption = None) -> N
65
56
  PathExtended(save_path).to_cloud(rel2home=True, cloud=cloud)
66
57
 
67
58
 
68
- @sync_app.command(no_args_is_help=True)
69
59
  def clone(directory: DirectoryArgument = None, cloud: CloudOption = None) -> None:
70
60
  """📥 Clone repositories described by a repos.json specification."""
71
61
  from machineconfig.scripts.python.repos_helpers.entrypoint import clone_from_specs
@@ -74,7 +64,6 @@ def clone(directory: DirectoryArgument = None, cloud: CloudOption = None) -> Non
74
64
  clone_from_specs(directory, cloud, checkout_branch_flag=False, checkout_commit_flag=False)
75
65
 
76
66
 
77
- @sync_app.command(name="checkout-to-commit", no_args_is_help=True)
78
67
  def checkout_command(directory: DirectoryArgument = None, cloud: CloudOption = None) -> None:
79
68
  """🔀 Check out specific commits listed in the specification."""
80
69
  from machineconfig.scripts.python.repos_helpers.entrypoint import clone_from_specs
@@ -83,14 +72,12 @@ def checkout_command(directory: DirectoryArgument = None, cloud: CloudOption = N
83
72
  clone_from_specs(directory, cloud, checkout_branch_flag=False, checkout_commit_flag=True)
84
73
 
85
74
 
86
- @sync_app.command(name="checkout-to-branch", no_args_is_help=True)
87
75
  def checkout_to_branch_command(directory: DirectoryArgument = None, cloud: CloudOption = None) -> None:
88
76
  """🔀 Check out to the main branch defined in the specification."""
89
77
  from machineconfig.scripts.python.repos_helpers.entrypoint import clone_from_specs
90
78
  clone_from_specs(directory, cloud, checkout_branch_flag=True, checkout_commit_flag=False)
91
79
 
92
80
 
93
- @app.command(no_args_is_help=True)
94
81
  def analyze(directory: DirectoryArgument = None) -> None:
95
82
  """📊 Analyze repository development over time."""
96
83
  repo_path = directory if directory is not None else "."
@@ -99,10 +86,6 @@ def analyze(directory: DirectoryArgument = None) -> None:
99
86
  analyze_repo_development(repo_path=repo_path)
100
87
 
101
88
 
102
- app.command(name="secure", no_args_is_help=True, help="🔐 Securely sync git repository to/from cloud with encryption")(secure_repo_main)
103
-
104
-
105
- @app.command(no_args_is_help=True)
106
89
  def viz(
107
90
  repo: str = typer.Option(Path.cwd().__str__(), "--repo", "-r", help="Path to git repository to visualize"),
108
91
  output_file: Optional[Path] = typer.Option(None, "--output", "-o", help="Output video file (e.g., output.mp4). If specified, gource will render to video."),
@@ -134,7 +117,7 @@ def viz(
134
117
  file_idle_time=file_idle_time, framerate=framerate, background_color=background_color,
135
118
  font_size=font_size, camera_mode=camera_mode)
136
119
 
137
- @app.command(no_args_is_help=True)
120
+
138
121
  def cleanup(repo: DirectoryArgument = None, recursive: RecursiveOption = False) -> None:
139
122
  """🧹 Clean repository directories from cache files."""
140
123
  if repo is None:
@@ -169,3 +152,38 @@ uv run --with cleanpy cleanpy .
169
152
  """
170
153
  from machineconfig.utils.code import run_shell_script
171
154
  run_shell_script(script)
155
+
156
+
157
+ def get_app():
158
+ repos_apps = typer.Typer(help="📁 [r] Manage development repositories", no_args_is_help=True)
159
+ mirror_app = typer.Typer(help="🔄 [m] Manage repository specifications and syncing", no_args_is_help=True)
160
+ repos_apps.add_typer(mirror_app, name="mirror", help="🔄 [m] mirror repositories using saved specs")
161
+ repos_apps.add_typer(mirror_app, name="m", help="mirror repositories using saved specs", hidden=True)
162
+
163
+ repos_apps.command(name="push", help="🚀 [p] Push changes across repositories")(push)
164
+ repos_apps.command(name="p", help="Push changes across repositories", hidden=True)(push)
165
+ repos_apps.command(name="pull", help="⬇️ [P] Pull changes across repositories")(pull)
166
+ repos_apps.command(name="P", help="Pull changes across repositories", hidden=True)(pull)
167
+ repos_apps.command(name="commit", help="💾 [c] Commit changes across repositories")(commit)
168
+ repos_apps.command(name="c", help="Commit changes across repositories", hidden=True)(commit)
169
+ repos_apps.command(name="sync", help="🔄 [s] Pull, commit, and push changes across repositories")(sync)
170
+ repos_apps.command(name="s", help="Pull, commit, and push changes across repositories", hidden=True)(sync)
171
+ repos_apps.command(name="analyze", help="📊 [a] Analyze repository development over time")(analyze)
172
+ repos_apps.command(name="a", help="Analyze repository development over time", hidden=True)(analyze)
173
+ repos_apps.command(name="secure", help="🔐 [s] Securely sync git repository to/from cloud with encryption")(secure_repo_main)
174
+ repos_apps.command(name="s", help="Securely sync git repository to/from cloud with encryption", hidden=True)(secure_repo_main)
175
+ repos_apps.command(name="viz", help="🎬 [v] Visualize repository activity using Gource")(viz)
176
+ repos_apps.command(name="v", help="Visualize repository activity using Gource", hidden=True)(viz)
177
+ repos_apps.command(name="cleanup", help="🧹 [n] Clean repository directories from cache files")(cleanup)
178
+ repos_apps.command(name="n", help="Clean repository directories from cache files", hidden=True)(cleanup)
179
+
180
+ mirror_app.command(name="capture", help="📝 [cap] Record repositories into a repos.json specification")(capture)
181
+ mirror_app.command(name="cap", help="Record repositories into a repos.json specification", hidden=True)(capture)
182
+ mirror_app.command(name="clone", help="📥 [clo] Clone repositories described by a repos.json specification")(clone)
183
+ mirror_app.command(name="clo", help="Clone repositories described by a repos.json specification", hidden=True)(clone)
184
+ mirror_app.command(name="checkout-to-commit", help="🔀 [ctc] Check out specific commits listed in the specification")(checkout_command)
185
+ mirror_app.command(name="ctc", help="Check out specific commits listed in the specification", hidden=True)(checkout_command)
186
+ mirror_app.command(name="checkout-to-branch", help="🔀 [ctb] Check out to the main branch defined in the specification")(checkout_to_branch_command)
187
+ mirror_app.command(name="ctb", help="Check out to the main branch defined in the specification", hidden=True)(checkout_to_branch_command)
188
+
189
+ return repos_apps
@@ -2,26 +2,20 @@
2
2
  import typer
3
3
  from typing import Optional
4
4
 
5
- cli_app = typer.Typer(help="🔄 SELF operations subcommands", no_args_is_help=True)
6
5
 
7
-
8
- @cli_app.command()
9
6
  def update():
10
7
  """🔄 UPDATE essential repos"""
11
8
  import machineconfig.scripts.python.devops_helpers.devops_update_repos as helper
12
9
  helper.main()
13
- @cli_app.command()
14
10
  def interactive():
15
11
  """🤖 INTERACTIVE configuration of machine."""
16
12
  from machineconfig.scripts.python.interactive import main
17
13
  main()
18
14
 
19
- @cli_app.command()
20
15
  def status():
21
16
  """📊 STATUS of machine, shell profile, apps, symlinks, dotfiles, etc."""
22
17
  import machineconfig.scripts.python.devops_helpers.devops_status as helper
23
18
  helper.main()
24
- @cli_app.command()
25
19
  def install():
26
20
  """📋 CLONE machienconfig locally and incorporate to shell profile for faster execution and nightly updates."""
27
21
  from machineconfig.utils.code import run_shell_script
@@ -31,21 +25,19 @@ def install():
31
25
  # main_public_from_parser()
32
26
  import platform
33
27
  if platform.system() == "Windows":
34
- run_shell_script(r"""$HOME\.local\bin\uv.exe tool install machineconfig>=5.65""")
28
+ run_shell_script(r"""$HOME\.local\bin\uv.exe tool install machineconfig>=5.67""")
35
29
  else:
36
- run_shell_script("""$HOME/.local/bin/uv tool install machineconfig>=5.65""")
30
+ run_shell_script("""$HOME/.local/bin/uv tool install machineconfig>=5.67""")
37
31
 
38
- @cli_app.command(no_args_is_help=False)
39
32
  def navigate():
40
33
  """📚 NAVIGATE command structure with TUI"""
41
34
  import machineconfig.scripts.python as navigator
42
35
  from pathlib import Path
43
36
  path = Path(navigator.__file__).resolve().parent.joinpath("devops_navigator.py")
44
37
  from machineconfig.utils.code import run_shell_script
45
- run_shell_script(f"uv run --with machineconfig>=5.65,textual {path}")
38
+ run_shell_script(f"uv run --with machineconfig>=5.67,textual {path}")
46
39
 
47
40
 
48
- @cli_app.command(no_args_is_help=True)
49
41
  def run_python(ip: str = typer.Argument(..., help="Python command to run in the machineconfig environment"),
50
42
  command: Optional[bool] = typer.Option(False, "--command", "-c", help="Run as command")):
51
43
  """🐍 RUN python command/file in the machineconfig environment"""
@@ -56,3 +48,19 @@ def run_python(ip: str = typer.Argument(..., help="Python command to run in the
56
48
  import subprocess
57
49
  import sys
58
50
  subprocess.run([sys.executable, ip], cwd=machineconfig.__path__[0])
51
+
52
+ def get_app():
53
+ cli_app = typer.Typer(help="🔄 [s] self operations subcommands", no_args_is_help=True)
54
+ cli_app.command("update", no_args_is_help=True, help="🔄 [u] UPDATE essential repos")(update)
55
+ cli_app.command("u", no_args_is_help=True, help="UPDATE essential repos", hidden=True)(update)
56
+ cli_app.command("interactive", no_args_is_help=True, help="🤖 [ia] INTERACTIVE configuration of machine.")(interactive)
57
+ cli_app.command("ia", no_args_is_help=True, help="INTERACTIVE configuration of machine.", hidden=True)(interactive)
58
+ cli_app.command("status", no_args_is_help=True, help="📊 [s] STATUS of machine, shell profile, apps, symlinks, dotfiles, etc.")(status)
59
+ cli_app.command("s", no_args_is_help=True, help="STATUS of machine, shell profile, apps, symlinks, dotfiles, etc.", hidden=True)(status)
60
+ cli_app.command("install", no_args_is_help=True, help="📋 [i] CLONE machienconfig locally and incorporate to shell profile for faster execution and nightly updates.")(install)
61
+ cli_app.command("i", no_args_is_help=True, help="CLONE machienconfig locally and incorporate to shell profile for faster execution and nightly updates.", hidden=True)(install)
62
+ cli_app.command("navigate", no_args_is_help=True, help="📚 [n] NAVIGATE command structure with TUI")(navigate)
63
+ cli_app.command("n", no_args_is_help=True, help="NAVIGATE command structure with TUI", hidden=True)(navigate)
64
+ cli_app.command("python", no_args_is_help=False, help="🐍 [c] python command/file in the machineconfig environment")(run_python)
65
+ cli_app.command("c", no_args_is_help=False, help="RUN python command/file in the machineconfig environment", hidden=True)(run_python)
66
+ return cli_app
@@ -105,7 +105,7 @@ def ftpx(
105
105
  from paramiko.ssh_exception import AuthenticationException # type: ignore
106
106
 
107
107
  try:
108
- ssh = SSH(rf"{machine}")
108
+ ssh = SSH(host=rf"{machine}", username=None, hostname=None, ssh_key_path=None, password=None, port=22, enable_compression=True)
109
109
  except AuthenticationException:
110
110
  console.print(
111
111
  Panel(
@@ -123,7 +123,7 @@ def ftpx(
123
123
  import getpass
124
124
 
125
125
  pwd = getpass.getpass()
126
- ssh = SSH(rf"{machine}", pwd=pwd)
126
+ ssh = SSH(host=rf"{machine}", username=None, hostname=None, ssh_key_path=None, password=pwd, port=22, enable_compression=True)
127
127
 
128
128
  if cloud:
129
129
  console.print(
@@ -133,7 +133,7 @@ def ftpx(
133
133
  border_style="cyan",
134
134
  )
135
135
  )
136
- ssh.run(f"cloud_copy {resolved_source} :^", desc="Uploading from remote to the cloud.").print()
136
+ ssh.run_shell(command=f"cloud_copy {resolved_source} :^", verbose_output=True, description="Uploading from remote to the cloud.", strict_stderr=False, strict_return_code=False)
137
137
  console.print(
138
138
  Panel.fit(
139
139
  "⬇️ Cloud transfer mode — downloading from cloud to local...",
@@ -141,7 +141,7 @@ def ftpx(
141
141
  border_style="cyan",
142
142
  )
143
143
  )
144
- ssh.run_locally(f"cloud_copy :^ {resolved_target}").print()
144
+ ssh.run_locally(command=f"cloud_copy :^ {resolved_target}")
145
145
  received_file = PathExtended(resolved_target) # type: ignore
146
146
  else:
147
147
  if source_is_remote:
@@ -163,7 +163,7 @@ def ftpx(
163
163
  padding=(1, 2),
164
164
  )
165
165
  )
166
- received_file = ssh.copy_to_here(source=resolved_source, target=resolved_target, z=zipFirst, r=recursive)
166
+ received_file = ssh.copy_to_here(source=resolved_source, target=resolved_target, compress_with_zip=zipFirst, recursive=recursive)
167
167
  else:
168
168
  assert resolved_source is not None, """
169
169
  ❌ Path Error: Target must be a remote path (machine:path)"""
@@ -183,7 +183,7 @@ def ftpx(
183
183
  padding=(1, 2),
184
184
  )
185
185
  )
186
- received_file = ssh.copy_from_here(source=resolved_source, target=resolved_target, z=zipFirst, r=recursive)
186
+ received_file = ssh.copy_from_here(source_path=resolved_source, target_path=resolved_target, compress_with_zip=zipFirst, recursive=recursive, overwrite_existing=False)
187
187
 
188
188
  if source_is_remote and isinstance(received_file, PathExtended):
189
189
  console.print(