machineconfig 5.66__py3-none-any.whl → 5.68__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.

@@ -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,17 @@ from machineconfig.scripts.python.helpers_fire.fire_agents_helper_types import A
9
9
 
10
10
 
11
11
  def create(
12
- context_path: Path = typer.Argument(..., help="Path to the context file"),
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/"),
13
17
  separator: str = typer.Option("\n", help="Separator for context"),
14
- tasks_per_prompt: int = typer.Option(13, help="Number of tasks per prompt"),
15
-
16
- agent: AGENTS = typer.Option(..., help=f"Agent type. One of {', '.join(get_args(AGENTS))}"),
17
- machine: MATCHINE = typer.Option(..., help=f"Machine to run agents on. One of {', '.join(get_args(MATCHINE))}"),
18
- model: MODEL = typer.Option(..., help=f"Model to use (for crush agent). One of {', '.join(get_args(MODEL))}"),
19
- provider: PROVIDER = typer.Option(..., help=f"Provider to use (for crush agent). One of {', '.join(get_args(PROVIDER))}"),
20
-
18
+ agent_load: int = typer.Option(13, help="Number of tasks per prompt"),
21
19
  prompt: Optional[str] = typer.Option(None, help="Prompt prefix as string"),
22
20
  prompt_path: Optional[Path] = typer.Option(None, help="Path to prompt file"),
23
21
  job_name: str = typer.Option("AI_Agents", help="Job name"),
24
- separate_prompt_from_context: bool = typer.Option(True, help="Keep prompt material in separate file to the context."),
22
+ separate: bool = typer.Option(True, help="Keep prompt material in separate file to the context."),
25
23
  output_path: Optional[Path] = typer.Option(None, help="Path to write the layout.json file"),
26
24
  agents_dir: Optional[Path] = typer.Option(None, help="Directory to store agent files. If not provided, will be constructed automatically."),
27
25
  ):
@@ -43,40 +41,49 @@ def create(
43
41
  raise typer.Exit(1)
44
42
  typer.echo(f"Operating @ {repo_root}")
45
43
 
46
- prompt_material_path = Path("")
44
+ if context_path is None:
45
+ context_path = repo_root / ".ai" / "todo"
46
+
47
+ context_path_resolved = context_path.expanduser().resolve()
48
+ if not context_path_resolved.exists():
49
+ raise typer.BadParameter(f"Path does not exist: {context_path_resolved}")
47
50
 
48
- target_file_path = context_path.expanduser().resolve()
49
- if not target_file_path.exists() or not target_file_path.is_file():
50
- raise typer.BadParameter(f"Invalid file path: {target_file_path}")
51
- prompt_material_path = target_file_path
51
+ if context_path_resolved.is_file():
52
+ prompt_material_re_splitted = chunk_prompts(context_path_resolved, tasks_per_prompt=agent_load, joiner=separator)
53
+ elif context_path_resolved.is_dir():
54
+ files = [f for f in context_path_resolved.rglob("*") if f.is_file()]
55
+ if not files:
56
+ raise typer.BadParameter(f"No files found in directory: {context_path_resolved}")
57
+ concatenated = separator.join(f.read_text(encoding="utf-8") for f in files)
58
+ prompt_material_re_splitted = [concatenated]
59
+ else:
60
+ raise typer.BadParameter(f"Path is neither file nor directory: {context_path_resolved}")
52
61
 
53
62
  if prompt_path is not None:
54
63
  prompt_prefix = prompt_path.read_text(encoding="utf-8")
55
64
  else:
56
65
  prompt_prefix = cast(str, prompt)
57
66
  agent_selected = agent
58
- keep_material_in_separate_file_input = separate_prompt_from_context
59
- prompt_material_re_splitted = chunk_prompts(prompt_material_path, tasks_per_prompt=tasks_per_prompt, joiner=separator)
60
67
  if agents_dir is None: agents_dir = repo_root / ".ai" / f"tmp_prompts/{job_name}_{randstr()}"
61
68
  else:
62
69
  import shutil
63
70
  if agents_dir.exists():
64
71
  shutil.rmtree(agents_dir)
65
72
  prep_agent_launch(repo_root=repo_root, agents_dir=agents_dir, prompts_material=prompt_material_re_splitted,
66
- keep_material_in_separate_file=keep_material_in_separate_file_input,
73
+ keep_material_in_separate_file=separate,
67
74
  prompt_prefix=prompt_prefix, machine=machine, agent=agent_selected, model=model, provider=provider,
68
75
  job_name=job_name)
69
76
  layoutfile = get_agents_launch_layout(session_root=agents_dir)
70
77
  regenerate_py_code = f"""
71
78
  #!/usr/bin/env uv run --python 3.14 --with machineconfig
72
- agents create "{prompt_material_path}" \\
79
+ agents create "{context_path_resolved}" \\
73
80
  --prompt-path "{prompt_path or ''}" \\
74
81
  --agent "{agent_selected}" \\
75
82
  --machine "{machine}" \\
76
83
  --job-name "{job_name}" \\
77
- --tasks-per-prompt {tasks_per_prompt} \\
84
+ --agent_load {agent_load} \\
78
85
  --separator "{separator}" \\
79
- {"--separate-prompt-from-context" if keep_material_in_separate_file_input else ""}
86
+ {"--separate" if separate else ""}
80
87
  """
81
88
  (agents_dir / "aa_agents_relaunch.py").write_text(data=regenerate_py_code, encoding="utf-8")
82
89
  layout_output_path = output_path if output_path is not None else agents_dir / "layout.json"
@@ -153,8 +160,18 @@ def init_config():
153
160
  add_ai_configs(repo_root=Path.cwd())
154
161
 
155
162
  def get_app():
156
- agents_app = typer.Typer(help="🤖 AI Agents management subcommands")
157
- agents_app.command("create", no_args_is_help=True, help="Create agents layout file, ready to run.")(create)
163
+ agents_app = typer.Typer(help="🤖 AI Agents management subcommands", no_args_is_help=True)
164
+ sep = "\n"
165
+ agents_full_help = f"""
166
+ Create agents layout file, ready to run.
167
+ {sep}
168
+ PROVIDER options: {', '.join(get_args(PROVIDER))}
169
+ {sep}
170
+ AGENT options: {', '.join(get_args(AGENTS))}
171
+ {sep}
172
+ MODEL options: {sep.join(get_args(MODEL))}
173
+ """
174
+ agents_app.command("create", no_args_is_help=True, help=agents_full_help)(create)
158
175
  agents_app.command("collect", no_args_is_help=True, help="Collect all agent materials into a single file.")(collect)
159
176
  agents_app.command("make-template", no_args_is_help=False, help="Create a template for fire agents")(template)
160
177
  agents_app.command("make-config", no_args_is_help=False, help="Initialize AI configurations in the current repository")(init_config)
@@ -164,14 +181,10 @@ def get_app():
164
181
  agents_app.command(name="make-symlinks", no_args_is_help=True, help="Create symlinks to the current repo in ~/code_copies/")(create_symlink_command)
165
182
  return agents_app
166
183
 
184
+
167
185
  def main():
168
186
  agents_app = get_app()
169
- import sys
170
- if len(sys.argv) == 1:
171
- agents_app(["--help"])
172
- else:
173
- agents_app()
174
-
187
+ agents_app()
175
188
 
176
189
  if __name__ == "__main__": # pragma: no cover
177
190
  pass
@@ -2,16 +2,17 @@
2
2
  """Script to generate a markdown table with checkboxes for all Python and shell files in the repo."""
3
3
 
4
4
  from pathlib import Path
5
- from typing import Annotated, Literal
5
+ from typing import Annotated, Literal, Optional
6
6
  from rich.console import Console
7
7
  from rich.panel import Panel
8
8
  import typer
9
9
  import subprocess
10
+ import shutil
10
11
 
11
12
 
12
13
  def get_python_files(repo_root: Path, exclude_init: bool = False) -> list[str]:
13
14
  """Get all Python files relative to repo root."""
14
- excluded_parts = {".venv", "__pycache__", ".git", "build", "dist"}
15
+ excluded_parts = {".venv", "__pycache__", ".git", "build", "dist", ".ai"}
15
16
  excluded_patterns = {"*.egg-info"}
16
17
 
17
18
  # Get all .py files recursively
@@ -112,6 +113,47 @@ def filter_files_by_content(repo_root: Path, files: list[str], keyword: str) ->
112
113
  return filtered_files
113
114
 
114
115
 
116
+ def generate_csv_content(python_files: list[str], shell_files: list[str], repo_root: Path, include_line_count: bool = False) -> str:
117
+ """Generate CSV content with file information."""
118
+ import csv
119
+ import io
120
+
121
+ output = io.StringIO()
122
+ writer = csv.writer(output)
123
+
124
+ # Write header
125
+ if include_line_count:
126
+ writer.writerow(["Type", "Index", "File Path", "Line Count", "Status"])
127
+ else:
128
+ writer.writerow(["Type", "Index", "File Path", "Status"])
129
+
130
+ # Write Python files
131
+ for index, file_path in enumerate(python_files, start=1):
132
+ clean_path = file_path.lstrip("./")
133
+ if include_line_count:
134
+ line_count = count_lines(repo_root / file_path)
135
+ writer.writerow(["Python", index, clean_path, line_count, "[ ]"])
136
+ else:
137
+ writer.writerow(["Python", index, clean_path, "[ ]"])
138
+
139
+ # Write shell files
140
+ for index, file_path in enumerate(shell_files, start=1):
141
+ clean_path = file_path.lstrip("./")
142
+ if include_line_count:
143
+ line_count = count_lines(repo_root / file_path)
144
+ writer.writerow(["Shell", index, clean_path, line_count, "[ ]"])
145
+ else:
146
+ writer.writerow(["Shell", index, clean_path, "[ ]"])
147
+
148
+ return output.getvalue()
149
+
150
+
151
+ def generate_txt_content(python_files: list[str], shell_files: list[str]) -> str:
152
+ """Generate plain text content with file paths."""
153
+ all_files = python_files + shell_files
154
+ return "\n".join(file.lstrip("./") for file in all_files)
155
+
156
+
115
157
  def generate_markdown_table(python_files: list[str], shell_files: list[str], repo_root: Path, include_line_count: bool = False) -> str:
116
158
  """Generate markdown table with checkboxes."""
117
159
  header = "# File Checklist\n\n"
@@ -159,6 +201,40 @@ def generate_markdown_table(python_files: list[str], shell_files: list[str], rep
159
201
  return header + content
160
202
 
161
203
 
204
+ def split_files_into_chunks(all_files: list[str], split_every: Optional[int] = None, split_to: Optional[int] = None) -> list[list[str]]:
205
+ """Split files into chunks based on split_every or split_to."""
206
+ if split_every is not None:
207
+ # Split into chunks of split_every files each
208
+ return [all_files[i:i + split_every] for i in range(0, len(all_files), split_every)]
209
+ elif split_to is not None:
210
+ # Split into exactly split_to chunks
211
+ if split_to <= 0:
212
+ return [all_files]
213
+ chunk_size = max(1, len(all_files) // split_to)
214
+ chunks = []
215
+ for i in range(split_to):
216
+ start = i * chunk_size
217
+ end = start + chunk_size if i < split_to - 1 else len(all_files)
218
+ chunks.append(all_files[start:end])
219
+ return chunks
220
+ else:
221
+ # No splitting
222
+ return [all_files]
223
+
224
+
225
+ def generate_content(python_files: list[str], shell_files: list[str], repo_root: Path,
226
+ format_type: str, include_line_count: bool) -> str:
227
+ """Generate content based on format type."""
228
+ if format_type == "csv":
229
+ return generate_csv_content(python_files, shell_files, repo_root, include_line_count)
230
+ elif format_type == "md":
231
+ return generate_markdown_table(python_files, shell_files, repo_root, include_line_count)
232
+ elif format_type == "txt":
233
+ return generate_txt_content(python_files, shell_files)
234
+ else:
235
+ raise ValueError(f"Unsupported format: {format_type}")
236
+
237
+
162
238
  def create_repo_symlinks(repo_root: Path) -> None:
163
239
  """Create 5 symlinks to repo_root at ~/code_copies/${repo_name}_copy_{i}."""
164
240
  repo_name: str = repo_root.name
@@ -174,22 +250,30 @@ def create_repo_symlinks(repo_root: Path) -> None:
174
250
  def main(
175
251
  pattern: Annotated[str, typer.Argument(help="Pattern or keyword to match files by")],
176
252
  repo: Annotated[str, typer.Argument(help="Repository path. Can be any directory within a git repository.")] = str(Path.cwd()),
177
- strategy: Annotated[Literal["name", "keywords"], typer.Option("--strategy", help="Strategy to filter files: 'name' for filename matching, 'keywords' for content matching")] = "name",
178
- exclude_init: Annotated[bool, typer.Option("--exclude-init", help="Exclude __init__.py files from the checklist")] = False,
179
- include_line_count: Annotated[bool, typer.Option("--line-count", help="Include line count column in the markdown table")] = False,
180
- output_path: Annotated[str, typer.Option("--output-path", help="Path to output the markdown file relative to repo root")] = ".ai/todo/all_files_with_index.md",
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("-x", "--exclude-init", help="Exclude __init__.py files from the checklist")] = True,
255
+ include_line_count: Annotated[bool, typer.Option("-l", "--line-count", help="Include line count column in the output")] = False,
256
+ output_path: Annotated[str, typer.Option("-o", "--output-path", help="Base path for output files relative to repo root")] = ".ai/todo/files",
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", "-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,
181
260
  ) -> None:
182
- """Generate markdown checklist with Python and shell script files in the repository filtered by pattern."""
261
+ """Generate checklist with Python and shell script files in the repository filtered by pattern."""
183
262
  repo_path = Path(repo).expanduser().absolute()
184
263
  if not is_git_repository(repo_path):
185
264
  console = Console()
186
265
  console.print(Panel(f"❌ ERROR | Not a git repository or not in a git repository: {repo_path}", border_style="bold red", expand=False))
187
266
  raise typer.Exit(code=1)
188
267
 
189
- output_file = repo_path / output_path
268
+ # Delete .ai/todo directory at the start
269
+ todo_dir = repo_path / ".ai" / "todo"
270
+ if todo_dir.exists():
271
+ shutil.rmtree(todo_dir)
272
+
273
+ output_base = repo_path / output_path
190
274
 
191
275
  # Ensure output directory exists
192
- output_file.parent.mkdir(parents=True, exist_ok=True)
276
+ output_base.parent.mkdir(parents=True, exist_ok=True)
193
277
 
194
278
  # Get Python and shell files
195
279
  python_files = get_python_files(repo_path, exclude_init=exclude_init)
@@ -206,20 +290,46 @@ def main(
206
290
  print(f"Repo path: {repo_path}")
207
291
  print(f"Strategy: {strategy}")
208
292
  print(f"Pattern: {pattern}")
293
+ print(f"Format: {format_type}")
209
294
  print(f"Found {len(python_files)} Python files")
210
295
  print(f"Found {len(shell_files)} Shell script files")
211
296
 
212
- # Generate markdown
213
- markdown_content = generate_markdown_table(python_files, shell_files, repo_path, include_line_count)
214
-
215
- # Write to file
216
- output_file.write_text(markdown_content)
297
+ # Combine all files for splitting
298
+ all_files = python_files + shell_files
299
+
300
+ # Split files into chunks
301
+ file_chunks = split_files_into_chunks(all_files, split_every, split_to)
302
+
303
+ # Determine file extension based on format
304
+ extension = {"csv": ".csv", "md": ".md", "txt": ".txt"}[format_type]
305
+
306
+ output_files = []
307
+ for i, chunk in enumerate(file_chunks):
308
+ # Split chunk back into python and shell files
309
+ chunk_python = [f for f in chunk if f in python_files]
310
+ chunk_shell = [f for f in chunk if f in shell_files]
311
+
312
+ # Generate content for this chunk
313
+ content = generate_content(chunk_python, chunk_shell, repo_path, format_type, include_line_count)
314
+
315
+ # Determine output file path
316
+ if len(file_chunks) == 1:
317
+ output_file = output_base.with_suffix(extension)
318
+ else:
319
+ output_file = output_base.parent / f"{output_base.name}_{i+1}{extension}"
320
+
321
+ # Write to file
322
+ output_file.write_text(content)
323
+ output_files.append(output_file)
217
324
 
218
325
  console = Console()
219
- console.print(Panel(f"""✅ SUCCESS | Markdown checklist generated successfully!
220
- 📄 File Location: {output_file}
326
+ success_msg = f"""✅ SUCCESS | Files generated successfully!
327
+ 📄 Output files: {', '.join(str(f.relative_to(repo_path)) for f in output_files)}
221
328
  🐍 Python files: {len(python_files)}
222
- 🔧 Shell files: {len(shell_files)}""", border_style="bold blue", expand=False))
329
+ 🔧 Shell files: {len(shell_files)}
330
+ 📊 Total chunks: {len(file_chunks)}"""
331
+
332
+ console.print(Panel(success_msg, border_style="bold blue", expand=False))
223
333
 
224
334
 
225
335
  def create_symlink_command(num: Annotated[int, typer.Argument(help="Number of symlinks to create (1-5).")] = 5) -> None:
@@ -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
@@ -48,7 +48,7 @@ def path():
48
48
  from pathlib import Path
49
49
  path = Path(navigator.__file__).resolve().parent.joinpath("path_manager_tui.py")
50
50
  from machineconfig.utils.code import run_shell_script
51
- run_shell_script(f"uv run --with machineconfig>=5.65,textual {path}")
51
+ run_shell_script(f"uv run --with machineconfig>=5.67,textual {path}")
52
52
 
53
53
  @config_apps.command(no_args_is_help=False)
54
54
  def pwsh_theme():
@@ -31,9 +31,9 @@ def install():
31
31
  # main_public_from_parser()
32
32
  import platform
33
33
  if platform.system() == "Windows":
34
- run_shell_script(r"""$HOME\.local\bin\uv.exe tool install machineconfig>=5.65""")
34
+ run_shell_script(r"""$HOME\.local\bin\uv.exe tool install machineconfig>=5.67""")
35
35
  else:
36
- run_shell_script("""$HOME/.local/bin/uv tool install machineconfig>=5.65""")
36
+ run_shell_script("""$HOME/.local/bin/uv tool install machineconfig>=5.67""")
37
37
 
38
38
  @cli_app.command(no_args_is_help=False)
39
39
  def navigate():
@@ -42,7 +42,7 @@ def navigate():
42
42
  from pathlib import Path
43
43
  path = Path(navigator.__file__).resolve().parent.joinpath("devops_navigator.py")
44
44
  from machineconfig.utils.code import run_shell_script
45
- 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}")
46
46
 
47
47
 
48
48
  @cli_app.command(no_args_is_help=True)
@@ -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, z=zipFirst, r=recursive, init=True)
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(
@@ -130,9 +130,9 @@ def execute_installations(selected_options: list[str]) -> None:
130
130
  console.print(Panel("🐍 [bold green]PYTHON ENVIRONMENT[/bold green]\n[italic]Virtual environment setup[/italic]", border_style="green"))
131
131
  import platform
132
132
  if platform.system() == "Windows":
133
- run_shell_script(r"""$HOME\.local\bin\uv.exe tool install machineconfig>=5.65""")
133
+ run_shell_script(r"""$HOME\.local\bin\uv.exe tool install machineconfig>=5.67""")
134
134
  else:
135
- run_shell_script("""$HOME/.local/bin/uv tool install machineconfig>=5.65""")
135
+ run_shell_script("""$HOME/.local/bin/uv tool install machineconfig>=5.67""")
136
136
  if "install_ssh_server" in selected_options:
137
137
  console.print(Panel("🔒 [bold red]SSH SERVER[/bold red]\n[italic]Remote access setup[/italic]", border_style="red"))
138
138
  import platform
@@ -5,7 +5,7 @@
5
5
  # mkdir ~/data/local
6
6
  # sudo mount -o nolock,noatime,nodiratime,proto=tcp,timeo=600,retrans=2,noac alex-p51s-5:/home/alex/data/local ./data/local
7
7
 
8
- uv run --python 3.14 --with machineconfig>=5.65python -m machineconfig.scripts.python.mount_nfs
8
+ uv run --python 3.14 --with machineconfig>=5.67python -m machineconfig.scripts.python.mount_nfs
9
9
  # Check if remote server is reachable and share folder exists
10
10
  if ! ping -c 1 "$remote_server" &> /dev/null; then
11
11
  echo "💥 Error: Remote server $remote_server is not reachable."
@@ -20,8 +20,8 @@ def main():
20
20
  tmp = choose_ssh_host(multi=False)
21
21
  assert isinstance(tmp, str)
22
22
  ssh = SSH(tmp)
23
- default = f"{ssh.hostname}:{ssh.run('echo $HOME').op}/data/share_nfs"
24
- share_info = choose_from_options(msg="📂 Choose a share path:", options=[f"{ssh.hostname}:{item.split(' ')[0]}" for item in ssh.run("cat /etc/exports").op.split("\n") if not item.startswith("#")] + [default], default=default, multi=False)
23
+ default = f"{ssh.hostname}:{ssh.run_shell('echo $HOME').op}/data/share_nfs"
24
+ share_info = choose_from_options(msg="📂 Choose a share path:", options=[f"{ssh.hostname}:{item.split(' ')[0]}" for item in ssh.run_shell("cat /etc/exports").op.split("\n") if not item.startswith("#")] + [default], default=default, multi=False)
25
25
  assert isinstance(share_info, str), f"❌ share_info must be a string. Got {type(share_info)}"
26
26
 
27
27
  remote_server = share_info.split(":")[0]
@@ -19,7 +19,7 @@ def main():
19
19
  tmp = choose_ssh_host(multi=False)
20
20
  assert isinstance(tmp, str)
21
21
  ssh = SSH(host=tmp)
22
- share_info = f"{ssh.username}@{ssh.hostname}:{ssh.run('echo $HOME').op}/data/share_ssh"
22
+ share_info = f"{ssh.username}@{ssh.hostname}:{ssh.run_shell('echo $HOME').op}/data/share_ssh"
23
23
  else:
24
24
  ssh = SSH(share_info.split(":")[0])
25
25
 
@@ -7,7 +7,7 @@ def analyze_repo_development(repo_path: str = typer.Argument(..., help="Path to
7
7
  from pathlib import Path
8
8
  count_lines_path = Path(count_lines.__file__)
9
9
  # --project $HOME/code/ machineconfig --group plot
10
- cmd = f"""uv run --python 3.14 --with machineconfig[plot]>=5.65 {count_lines_path} analyze-over-time {repo_path}"""
10
+ cmd = f"""uv run --python 3.14 --with machineconfig[plot]>=5.67 {count_lines_path} analyze-over-time {repo_path}"""
11
11
  from machineconfig.utils.code import run_shell_script
12
12
  run_shell_script(cmd)
13
13
 
@@ -141,7 +141,7 @@ def get_app():
141
141
  layouts_app.command("create-from-function", no_args_is_help=True, help="Create a layout from a function")(create_from_function)
142
142
  layouts_app.command("run", no_args_is_help=True, help="Run the selected layout(s)")(run)
143
143
  layouts_app.command("balance-load", no_args_is_help=True, help="Balance the load across sessions")(balance_load)
144
- layouts_app.command("kill-process", no_args_is_help=True, help="Choose a process to kill")(kill_process)
144
+ layouts_app.command("kill-process", no_args_is_help=False, help="Choose a process to kill")(kill_process)
145
145
  return layouts_app
146
146
 
147
147
 
@@ -7,7 +7,7 @@ $user = ''
7
7
  $sharePath = ''
8
8
  $driveLetter = ''
9
9
 
10
- uv run --python 3.14 --with machineconfig>=5.65python -m machineconfig.scripts.python.mount_ssh
10
+ uv run --python 3.14 --with machineconfig>=5.67python -m machineconfig.scripts.python.mount_ssh
11
11
 
12
12
  net use T: \\sshfs.kr\$user@$host.local
13
13
  # this worked: net use T: \\sshfs\alex@alex-p51s-5.local
@@ -14,7 +14,6 @@ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
14
14
  echo "🔧 Configuring NVM environment..."
15
15
  export NVM_DIR="$HOME/.nvm"
16
16
  [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
17
- [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
18
17
  echo "📥 Installing latest Node.js..."
19
18
  nvm install node || true
20
19
  echo "📥 Installing SQLite - lightweight SQL database..."
@@ -1,25 +1,25 @@
1
1
  #!/bin/bash
2
2
  . <( curl -sSL "https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/setup_linux/uv.sh")
3
3
  devops() {
4
- "$HOME/.local/bin/uv" run --python 3.14 --with machineconfig>=5.65 devops "$@"
4
+ "$HOME/.local/bin/uv" run --python 3.14 --with machineconfig>=5.67 devops "$@"
5
5
  }
6
6
  agents() {
7
- "$HOME/.local/bin/uv" run --python 3.14 --with machineconfig>=5.65 agents "$@"
7
+ "$HOME/.local/bin/uv" run --python 3.14 --with machineconfig>=5.67 agents "$@"
8
8
  }
9
9
  cloud() {
10
- "$HOME/.local/bin/uv" run --python 3.14 --with machineconfig>=5.65 cloud "$@"
10
+ "$HOME/.local/bin/uv" run --python 3.14 --with machineconfig>=5.67 cloud "$@"
11
11
  }
12
12
  croshell() {
13
- "$HOME/.local/bin/uv" run --python 3.14 --with machineconfig>=5.65 croshell "$@"
13
+ "$HOME/.local/bin/uv" run --python 3.14 --with machineconfig>=5.67 croshell "$@"
14
14
  }
15
15
  fire() {
16
- "$HOME/.local/bin/uv" run --python 3.14 --with machineconfig>=5.65fire "$@"
16
+ "$HOME/.local/bin/uv" run --python 3.14 --with machineconfig>=5.67fire "$@"
17
17
  }
18
18
  ftpx() {
19
- "$HOME/.local/bin/uv" run --python 3.14 --with machineconfig>=5.65ftpx "$@"
19
+ "$HOME/.local/bin/uv" run --python 3.14 --with machineconfig>=5.67ftpx "$@"
20
20
  }
21
21
  sessions() {
22
- "$HOME/.local/bin/uv" run --python 3.14 --with machineconfig>=5.65sessions "$@"
22
+ "$HOME/.local/bin/uv" run --python 3.14 --with machineconfig>=5.67sessions "$@"
23
23
  }
24
24
 
25
25
  echo "devops command is now defined in this shell session."
@@ -2,30 +2,30 @@
2
2
 
3
3
  iex (iwr "https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/setup_windows/uv.ps1").Content
4
4
  function devops {
5
- & "$HOME\.local\bin\uv.exe" run --python 3.14 --with machineconfig>=5.65 devops $args
5
+ & "$HOME\.local\bin\uv.exe" run --python 3.14 --with machineconfig>=5.67 devops $args
6
6
  }
7
7
 
8
8
  function cloud {
9
- & "$HOME\.local\bin\uv.exe" run --python 3.14 --with machineconfig>=5.65 cloud $args
9
+ & "$HOME\.local\bin\uv.exe" run --python 3.14 --with machineconfig>=5.67 cloud $args
10
10
  }
11
11
 
12
12
  function croshell {
13
- & "$HOME\.local\bin\uv.exe" run --python 3.14 --with machineconfig>=5.65 croshell $args
13
+ & "$HOME\.local\bin\uv.exe" run --python 3.14 --with machineconfig>=5.67 croshell $args
14
14
  }
15
15
 
16
16
  function agents {
17
- & "$HOME\.local\bin\uv.exe" run --python 3.14 --with machineconfig>=5.65 agents $args
17
+ & "$HOME\.local\bin\uv.exe" run --python 3.14 --with machineconfig>=5.67 agents $args
18
18
  }
19
19
 
20
20
  function fire {
21
- & "$HOME\.local\bin\uv.exe" run --python 3.14 --with machineconfig>=5.65 fire $args
21
+ & "$HOME\.local\bin\uv.exe" run --python 3.14 --with machineconfig>=5.67 fire $args
22
22
  }
23
23
 
24
24
  function ftpx {
25
- & "$HOME\.local\bin\uv.exe" run --python 3.14 --with machineconfig>=5.65 ftpx $args
25
+ & "$HOME\.local\bin\uv.exe" run --python 3.14 --with machineconfig>=5.67 ftpx $args
26
26
  }
27
27
 
28
28
  function sessions {
29
- & "$HOME\.local\bin\uv.exe" run --python 3.14 --with machineconfig>=5.65 sessions $args
29
+ & "$HOME\.local\bin\uv.exe" run --python 3.14 --with machineconfig>=5.67 sessions $args
30
30
  }
31
31