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

@@ -24,16 +24,8 @@ TMP_SERIALIZATION_DIR = Path.home().joinpath("tmp_results", "session_manager", "
24
24
  class ZellijLocalManager:
25
25
  """Manages multiple local zellij sessions and monitors their tabs and processes."""
26
26
 
27
- def __init__(self, session_layouts: list[LayoutConfig], session_name_prefix: str = "LocalJobMgr"):
28
- """
29
- Initialize the local zellij manager.
30
-
31
- Args:
32
- session_layouts: Dict mapping session names to their layout configs
33
- Format: {session_name: LayoutConfig, ...}
34
- session_name_prefix: Prefix for session names
35
- """
36
- self.session_name_prefix = session_name_prefix
27
+ def __init__(self, session_layouts: list[LayoutConfig], ):
28
+ self.session_name_prefix = "LocalJobMgr"
37
29
  self.session_layouts = session_layouts # Store the original config
38
30
  self.managers: List[ZellijLayoutGenerator] = []
39
31
 
@@ -368,16 +360,8 @@ class ZellijLocalManager:
368
360
  with open(config_file, "r", encoding="utf-8") as f:
369
361
  session_layouts = json.load(f)
370
362
 
371
- # Load metadata
372
- metadata_file = session_dir / "metadata.json"
373
- session_name_prefix = "LocalJobMgr" # default fallback
374
- if metadata_file.exists():
375
- with open(metadata_file, "r", encoding="utf-8") as f:
376
- metadata = json.load(f)
377
- session_name_prefix = metadata.get("session_name_prefix", "LocalJobMgr")
378
-
379
363
  # Create new instance
380
- instance = cls(session_layouts=session_layouts, session_name_prefix=session_name_prefix)
364
+ instance = cls(session_layouts=session_layouts)
381
365
 
382
366
  # Load saved manager states
383
367
  managers_dir = session_dir / "managers"
@@ -498,7 +482,7 @@ if __name__ == "__main__":
498
482
  ]
499
483
  try:
500
484
  # Create the local manager
501
- manager = ZellijLocalManager(sample_sessions, session_name_prefix="DevEnv")
485
+ manager = ZellijLocalManager(sample_sessions,)
502
486
  print(f"✅ Local manager created with {len(manager.managers)} sessions")
503
487
 
504
488
  # Show session names
@@ -1,6 +1,6 @@
1
- from pathlib import Path
2
- from typing import Optional
3
1
 
2
+ from pathlib import Path
3
+ from machineconfig.utils.ve import get_repo_root
4
4
 
5
5
  installations = """
6
6
  uv add --upgrade-package pylint pyright mypy pyrefly ty --dev # linters and type checkers
@@ -8,18 +8,6 @@ uv add --upgrade-package pytest --dev
8
8
  """
9
9
 
10
10
 
11
- def get_repo_root(path: Path) -> Optional[Path]:
12
- from git import Repo, InvalidGitRepositoryError
13
-
14
- try:
15
- repo = Repo(path, search_parent_directories=True)
16
- root = repo.working_tree_dir
17
- if root is not None:
18
- return Path(root)
19
- except InvalidGitRepositoryError:
20
- pass
21
- return None
22
-
23
11
 
24
12
  def add_ai_configs(repo_root: Path) -> None:
25
13
  import machineconfig as mc
@@ -73,8 +73,6 @@ def _display_summary(results: list[RepositoryUpdateResult]) -> None:
73
73
  changes = []
74
74
  if result["pyproject_changed"]:
75
75
  changes.append("pyproject.toml")
76
- if result["uv_lock_changed"]:
77
- changes.append("uv.lock")
78
76
  print(f" 📋 Dependencies changed: {', '.join(changes)}")
79
77
 
80
78
  if result["uv_sync_ran"]:
@@ -156,7 +154,6 @@ def main(verbose: bool = True, allow_password_prompt: bool = False) -> None:
156
154
  "commit_after": "",
157
155
  "commits_changed": False,
158
156
  "pyproject_changed": False,
159
- "uv_lock_changed": False,
160
157
  "dependencies_changed": False,
161
158
  "uv_sync_ran": False,
162
159
  "uv_sync_success": False,
@@ -171,7 +168,7 @@ def main(verbose: bool = True, allow_password_prompt: bool = False) -> None:
171
168
  Exception: {ex}
172
169
  {"-" * 50}""")
173
170
 
174
- # Run uv sync for repositories where pyproject.toml or uv.lock changed but sync wasn't run yet
171
+ # Run uv sync for repositories where pyproject.toml changed but sync wasn't run yet
175
172
  for repo_path in repos_with_changes:
176
173
  run_uv_sync(repo_path)
177
174
 
@@ -7,45 +7,19 @@ Improved design notes:
7
7
  * Preserves original core behavior & command generation for each agent type.
8
8
  """
9
9
 
10
- from __future__ import annotations
11
-
12
10
  from pathlib import Path
13
- from math import ceil
14
- from typing import Literal, TypeAlias, get_args, Iterable
11
+ from typing import cast, get_args, Iterable, TypeAlias, Literal
12
+ import json
13
+ import sys
15
14
 
16
- from machineconfig.cluster.sessions_managers.zellij_local_manager import ZellijLocalManager
17
- from machineconfig.scripts.python.fire_agents_help_launch import launch_agents
15
+ from machineconfig.scripts.python.fire_agents_help_launch import prep_agent_launch, get_agents_launch_layout, AGENTS
18
16
  from machineconfig.scripts.python.fire_agents_help_search import search_files_by_pattern, search_python_files
19
- from machineconfig.utils.schemas.layouts.layout_types import LayoutConfig
20
- # import time
21
-
22
- AGENTS: TypeAlias = Literal[
23
- "cursor-agent", "gemini", "crush", "q", "onlyPrepPromptFiles"
24
- # warp terminal
25
- ]
26
-
27
- SPLITTING_STRATEGY: TypeAlias = Literal[
28
- "agent_cap", # User decides number of agents, rows/tasks determined automatically
29
- "task_rows" # User decides number of rows/tasks, number of agents determined automatically
30
- ]
31
-
32
- DEFAULT_AGENT_CAP = 6
33
-
34
-
35
- def get_gemini_api_keys() -> list[str]:
36
- from machineconfig.utils.utils2 import read_ini
17
+ from machineconfig.scripts.python.fire_agents_load_balancer import chunk_prompts, SPLITTING_STRATEGY, DEFAULT_AGENT_CAP
18
+ from machineconfig.cluster.sessions_managers.zellij_local_manager import ZellijLocalManager
19
+ from machineconfig.utils.options import choose_one_option
20
+ from machineconfig.utils.ve import get_repo_root
37
21
 
38
- config = read_ini(Path.home().joinpath("dotfiles/creds/llm/gemini/api_keys.ini"))
39
- res: list[str] = []
40
- for a_section_name in list(config.sections()):
41
- a_section = config[a_section_name]
42
- if "api_key" in a_section:
43
- api_key = a_section["api_key"].strip()
44
- if api_key:
45
- res.append(api_key)
46
- # res = [v for k, v in config.items("api_keys") if k.startswith("key") and v.strip() != ""]
47
- print(f"Found {len(res)} Gemini API keys configured.")
48
- return res
22
+ SEARCH_STRATEGIES: TypeAlias = Literal["file_path", "keyword_search", "filename_pattern"]
49
23
 
50
24
 
51
25
  def _write_list_file(target: Path, files: Iterable[Path]) -> None:
@@ -53,139 +27,121 @@ def _write_list_file(target: Path, files: Iterable[Path]) -> None:
53
27
  target.write_text("\n".join(str(f) for f in files), encoding="utf-8")
54
28
 
55
29
 
56
- def _chunk_prompts(prompts: list[str], strategy: SPLITTING_STRATEGY, *, agent_cap: int | None, task_rows: int | None) -> list[str]:
57
- """Chunk prompts based on splitting strategy.
58
-
59
- Args:
60
- prompts: List of prompts to chunk
61
- strategy: Either 'agent_cap' or 'task_rows'
62
- agent_cap: Maximum number of agents (used with 'agent_cap' strategy)
63
- task_rows: Number of rows/tasks per agent (used with 'task_rows' strategy)
64
- """
65
- prompts = [p for p in prompts if p.strip() != ""] # drop blank entries
66
-
67
- if strategy == "agent_cap":
68
- if agent_cap is None:
69
- raise ValueError("agent_cap must be provided when using 'agent_cap' strategy")
70
-
71
- if len(prompts) <= agent_cap:
72
- return prompts
73
-
74
- print(f"Chunking {len(prompts)} prompts into groups for up to {agent_cap} agents because it exceeds the cap.")
75
- chunk_size = ceil(len(prompts) / agent_cap)
76
- grouped: list[str] = []
77
- for i in range(0, len(prompts), chunk_size):
78
- grouped.append("\nTargeted Locations:\n".join(prompts[i : i + chunk_size]))
79
- return grouped
80
-
81
- elif strategy == "task_rows":
82
- if task_rows is None:
83
- raise ValueError("task_rows must be provided when using 'task_rows' strategy")
84
-
85
- if task_rows >= len(prompts):
86
- return prompts
87
-
88
- print(f"Chunking {len(prompts)} prompts into groups of {task_rows} rows/tasks each.")
89
- grouped: list[str] = []
90
- for i in range(0, len(prompts), task_rows):
91
- grouped.append("\nTargeted Locations:\n".join(prompts[i : i + task_rows]))
92
- return grouped
93
-
94
- else:
95
- raise ValueError(f"Unknown splitting strategy: {strategy}")
96
-
97
-
98
-
99
- def main(): # noqa: C901 - (complexity acceptable for CLI glue)
100
- repo_root = Path.cwd()
101
- print(f"Operating @ {repo_root}")
102
-
103
- from machineconfig.utils.options import choose_one_option
104
-
105
- # Prompt user to choose search strategy
106
- search_strategies = ["file_path", "keyword_search", "filename_pattern"]
107
- search_strategy = choose_one_option(
108
- header="Choose search strategy:",
109
- options=search_strategies
110
- )
111
-
112
- # Execute chosen search strategy
30
+ def get_prompt_material(search_strategy: SEARCH_STRATEGIES, repo_root: Path) -> tuple[Path, str]:
113
31
  if search_strategy == "file_path":
114
32
  file_path_input = input("Enter path to target file: ").strip()
115
33
  if not file_path_input:
116
34
  print("No file path provided. Exiting.")
117
- return
35
+ sys.exit(1)
118
36
  target_file_path = Path(file_path_input).expanduser().resolve()
119
37
  if not target_file_path.exists() or not target_file_path.is_file():
120
38
  print(f"Invalid file path: {target_file_path}")
121
- return
39
+ sys.exit(1)
122
40
  separator = input("Enter separator [\\n]: ").strip() or "\n"
123
- source_text = target_file_path.read_text(encoding="utf-8", errors="ignore")
124
-
125
41
  elif search_strategy == "keyword_search":
126
42
  keyword = input("Enter keyword to search recursively for all .py files containing it: ").strip()
127
43
  if not keyword:
128
44
  print("No keyword supplied. Exiting.")
129
- return
45
+ sys.exit(1)
130
46
  matching_files = search_python_files(repo_root, keyword)
131
47
  if not matching_files:
132
48
  print(f"💥 No .py files found containing keyword: {keyword}")
133
- return
49
+ sys.exit(1)
134
50
  for idx, mf in enumerate(matching_files):
135
51
  print(f"{idx:>3}: {mf}")
136
52
  print(f"\nFound {len(matching_files)} .py files containing keyword: {keyword}")
137
- target_list_file = repo_root / ".ai" / "target_file.txt"
138
- _write_list_file(target_list_file, matching_files)
53
+ target_file_path = repo_root / ".ai" / "target_file.txt"
54
+ _write_list_file(target_file_path, matching_files)
139
55
  separator = "\n"
140
- source_text = target_list_file.read_text(encoding="utf-8", errors="ignore")
141
-
142
56
  elif search_strategy == "filename_pattern":
143
57
  pattern = input("Enter filename pattern (e.g., '*.py', '*test*', 'config.*'): ").strip()
144
58
  if not pattern:
145
59
  print("No pattern supplied. Exiting.")
146
- return
60
+ sys.exit(1)
147
61
  matching_files = search_files_by_pattern(repo_root, pattern)
148
62
  if not matching_files:
149
63
  print(f"💥 No files found matching pattern: {pattern}")
150
- return
64
+ sys.exit(1)
151
65
  for idx, mf in enumerate(matching_files):
152
66
  print(f"{idx:>3}: {mf}")
153
67
  print(f"\nFound {len(matching_files)} files matching pattern: {pattern}")
154
- target_list_file = repo_root / ".ai" / "target_file.txt"
155
- _write_list_file(target_list_file, matching_files)
68
+ target_file_path = repo_root / ".ai" / "target_file.txt"
69
+ _write_list_file(target_file_path, matching_files)
156
70
  separator = "\n"
157
- source_text = target_list_file.read_text(encoding="utf-8", errors="ignore")
158
71
  else:
159
72
  raise ValueError(f"Unknown search strategy: {search_strategy}")
160
- raw_prompts = source_text.split(separator)
161
- print(f"Loaded {len(raw_prompts)} raw prompts from source.")
162
- prefix = input("Enter prefix prompt: ")
163
- # Prompt user for splitting strategy
164
- splitting_strategy = choose_one_option(header="Select splitting strategy", options=get_args(SPLITTING_STRATEGY))
165
- # Get parameters based on strategy
166
- if splitting_strategy == "agent_cap":
167
- agent_cap_input = input(f"Enter maximum number of agents/splits [default: {DEFAULT_AGENT_CAP}]: ").strip()
168
- agent_cap = int(agent_cap_input) if agent_cap_input else DEFAULT_AGENT_CAP
169
- combined_prompts = _chunk_prompts(raw_prompts, splitting_strategy, agent_cap=agent_cap, task_rows=None)
170
- max_agents_for_launch = agent_cap
171
- elif splitting_strategy == "task_rows":
172
- task_rows_input = input("Enter number of rows/tasks per agent: ").strip()
173
- if not task_rows_input:
174
- print("Number of rows/tasks is required for this strategy.")
175
- return
176
- task_rows = int(task_rows_input)
177
- combined_prompts = _chunk_prompts(raw_prompts, splitting_strategy, agent_cap=None, task_rows=task_rows)
178
- max_agents_for_launch = len(combined_prompts) # Number of agents determined by chunking
179
- else:
180
- raise ValueError(f"Unknown splitting strategy: {splitting_strategy}")
181
- combined_prompts = [prefix + "\n" + p for p in combined_prompts]
182
- agent_selected = choose_one_option(header="Select agent type", options=get_args(AGENTS))
183
- tab_config = launch_agents(repo_root=repo_root, prompts=combined_prompts, agent=agent_selected, max_agents=max_agents_for_launch)
184
- if not tab_config:
185
- return
186
- from machineconfig.utils.utils2 import randstr
187
- random_name = randstr(length=3)
188
- manager = ZellijLocalManager(session_layouts=[LayoutConfig(layoutName="Agents", layoutTabs=tab_config)], session_name_prefix=random_name)
73
+ return target_file_path, separator
74
+
75
+
76
+ def main(): # noqa: C901 - (complexity acceptable for CLI glue)
77
+ repo_root = get_repo_root(Path.cwd())
78
+ if repo_root is None:
79
+ print("💥 Could not determine the repository root. Please run this script from within a git repository.")
80
+ sys.exit(1)
81
+ print(f"Operating @ {repo_root}")
82
+
83
+ search_strategy = cast(SEARCH_STRATEGIES, choose_one_option(header="Choose search strategy:", options=get_args(SEARCH_STRATEGIES)))
84
+ splitting_strategy = cast(SPLITTING_STRATEGY, choose_one_option(header="Choose prompt splitting strategy:", options=get_args(SPLITTING_STRATEGY)))
85
+ agent_selected = cast(AGENTS, choose_one_option(header="Select agent type", options=get_args(AGENTS)))
86
+ print("Enter prefix prompt (end with Ctrl-D / Ctrl-Z):")
87
+ prompt_prefix = "\n".join(sys.stdin.readlines())
88
+ job_name = input("Enter job name [AI_AGENTS]: ") or "AI_Agents"
89
+ keep_material_in_separate_file_input = input("Keep prompt material in separate file? [y/N]: ").strip().lower() == "y"
90
+
91
+ prompt_material_path, separator = get_prompt_material(search_strategy=search_strategy, repo_root=repo_root)
92
+ match splitting_strategy:
93
+ case "agent_cap":
94
+ agent_cap_input = input(f"Enter maximum number of agents/splits [default: {DEFAULT_AGENT_CAP}]: ").strip()
95
+ agent_cap = int(agent_cap_input) if agent_cap_input else DEFAULT_AGENT_CAP
96
+ task_rows = None
97
+ case "task_rows":
98
+ task_rows_input: str = input("Enter number of rows/tasks per agent [13]: ").strip() or "13"
99
+ task_rows = int(task_rows_input)
100
+ agent_cap = None
101
+ prompt_material_re_splitted = chunk_prompts(prompt_material_path, splitting_strategy, agent_cap=agent_cap, task_rows=task_rows, joiner=separator)
102
+
103
+ agents_dir = prep_agent_launch(repo_root=repo_root, prompts_material=prompt_material_re_splitted, keep_material_in_separate_file=keep_material_in_separate_file_input, prompt_prefix=prompt_prefix, agent=agent_selected, job_name=job_name)
104
+ layoutfile = get_agents_launch_layout(session_root=agents_dir)
105
+
106
+ regenerate_py_code = f"""
107
+ #!/usr/bin/env uv run --python 3.13 --with machineconfig
108
+ #!/usr/bin/env uv run --project $HOME/code/machineconfig
109
+
110
+ from machineconfig.scripts.python.fire_agents import *
111
+
112
+ repo_root = Path("{repo_root}")
113
+ search_strategy = "{search_strategy}"
114
+ splitting_strategy = "{splitting_strategy}"
115
+ agent_selected = "{agent_selected}"
116
+ prompt_prefix = '''{prompt_prefix}'''
117
+ job_name = "{job_name}"
118
+ keep_material_in_separate_file_input = {keep_material_in_separate_file_input}
119
+ separator = "{separator}"
120
+ prompt_material_path = Path("{prompt_material_path}")
121
+ agent_cap = {agent_cap}
122
+ task_rows = {task_rows}
123
+
124
+ prompt_material_re_splitted = chunk_prompts(prompt_material_path, splitting_strategy, agent_cap=agent_cap, task_rows=task_rows, joiner=separator)
125
+ agents_dir = prep_agent_launch(repo_root=repo_root, prompts_material=prompt_material_re_splitted, keep_material_in_separate_file=keep_material_in_separate_file_input, prompt_prefix=prompt_prefix, agent=agent_selected, job_name=job_name)
126
+ layout = get_agents_launch_layout(session_root=agents_dir)
127
+
128
+ (agents_dir / "aa_agents_relaunch.py").write_text(data=regenerate_py_code, encoding="utf-8")
129
+ (agents_dir / "layout.json").write_text(data=json.dumps(layout, indent=2), encoding="utf-8")
130
+
131
+ if len(layout["layoutTabs"]) > 25:
132
+ print("Too many agents (>25) to launch. Skipping launch.")
133
+ sys.exit(0)
134
+ manager = ZellijLocalManager(session_layouts=[layout])
135
+ manager.start_all_sessions()
136
+ manager.run_monitoring_routine()
137
+
138
+ """
139
+ (agents_dir / "aa_agents_relaunch.py").write_text(data=regenerate_py_code, encoding="utf-8")
140
+ (agents_dir / "layout.json").write_text(data=json.dumps(layoutfile, indent=2), encoding="utf-8")
141
+ if len(layoutfile["layouts"][0]["layoutTabs"]) > 25:
142
+ print("Too many agents (>25) to launch. Skipping launch.")
143
+ sys.exit(0)
144
+ manager = ZellijLocalManager(session_layouts=layoutfile["layouts"])
189
145
  manager.start_all_sessions()
190
146
  manager.run_monitoring_routine()
191
147
 
@@ -1,44 +1,79 @@
1
- from machineconfig.scripts.python.fire_agents import AGENTS, get_gemini_api_keys
2
- from machineconfig.utils.schemas.layouts.layout_types import TabConfig
1
+
3
2
  from machineconfig.utils.utils2 import randstr
4
3
 
5
4
  import random
6
5
  import shlex
7
6
  from pathlib import Path
7
+ from typing import Literal, TypeAlias
8
8
 
9
- def _confirm(message: str, default_no: bool = False) -> bool:
10
- from rich.prompt import Confirm
11
- return Confirm.ask(message, default=not default_no)
12
9
 
10
+ AGENTS: TypeAlias = Literal[
11
+ "cursor-agent", "gemini", "crush", "q", "onlyPrepPromptFiles"
12
+ # warp terminal
13
+ ]
14
+ AGENT_NAME_FORMATTER = "agent_{idx}_cmd.sh" # e.g., agent_0_cmd.sh
13
15
 
14
- def launch_agents(repo_root: Path, prompts: list[str], agent: AGENTS, *, max_agents: int) -> list[TabConfig]:
15
- """Create tab configuration for a set of agent prompts.
16
16
 
17
- If number of prompts exceeds max_agents, ask user for confirmation.
18
- (Original behavior raised an error; now interactive override.)
19
- """
20
- if not prompts:
21
- raise ValueError("No prompts provided")
17
+ def get_gemini_api_keys() -> list[str]:
18
+ from machineconfig.utils.utils2 import read_ini
19
+ config = read_ini(Path.home().joinpath("dotfiles/creds/llm/gemini/api_keys.ini"))
20
+ res: list[str] = []
21
+ for a_section_name in list(config.sections()):
22
+ a_section = config[a_section_name]
23
+ if "api_key" in a_section:
24
+ api_key = a_section["api_key"].strip()
25
+ if api_key:
26
+ res.append(api_key)
27
+ print(f"Found {len(res)} Gemini API keys configured.")
28
+ return res
22
29
 
23
- if len(prompts) > max_agents:
24
- proceed = _confirm(message=(f"You are about to launch {len(prompts)} agents which exceeds the cap ({max_agents}). Proceed?"), default_no=True)
25
- if not proceed:
26
- print("Aborting per user choice.")
27
- return []
30
+ def prep_agent_launch(repo_root: Path, prompts_material: list[str], prompt_prefix: str, keep_material_in_separate_file: bool, agent: AGENTS, *, job_name: str) -> Path:
31
+ session_root = repo_root / ".ai" / f"tmp_prompts/{job_name}_{randstr()}"
32
+ session_root.mkdir(parents=True, exist_ok=True)
33
+ prompt_folder = session_root / "prompts"
34
+ prompt_folder.mkdir(parents=True, exist_ok=True)
28
35
 
29
- tab_config: list[TabConfig] = []
30
- tmp_dir = repo_root / ".ai" / f"tmp_prompts/{randstr()}"
31
- tmp_dir.mkdir(parents=True, exist_ok=True)
36
+ all_materials_scripts: list[Path] = []
37
+ for idx, a_prompt_material in enumerate(prompts_material):
38
+ prompt_root = prompt_folder / f"agent_{idx}"
39
+ prompt_root.mkdir(parents=True, exist_ok=True)
40
+ prompt_path = prompt_root / f"agent_{idx}_prompt.txt"
41
+ if keep_material_in_separate_file:
42
+ prompt_material_path = prompt_root / f"agent_{idx}_material.txt"
43
+ prompt_material_path.write_text(a_prompt_material, encoding="utf-8")
44
+ prompt_path.write_text(prompt_prefix + f"""\nPlease only look @ {prompt_material_path}. You don't need to do any other work beside the content of this material file.""", encoding="utf-8")
45
+ all_materials_scripts.append(prompt_material_path)
46
+ else:
47
+ prompt_material_path = prompt_path
48
+ prompt_path.write_text(prompt_prefix + """\nPlease only look @ the following:\n""" + a_prompt_material, encoding="utf-8")
49
+
50
+ agent_cmd_launch_path = prompt_root / AGENT_NAME_FORMATTER.format(idx=idx) # e.g., agent_0_cmd.sh
51
+ random_sleep_time = random.uniform(0, 5)
52
+ cmd_prefix = f"""
53
+ #!/usr/bin/env bash
54
+
55
+ # AGENT-{idx}-LAUNCH-SCRIPT
56
+ # Auto-generated by fire_agents.py
57
+
58
+ export FIRE_AGENTS_AGENT_NAME="{agent}"
59
+ export FIRE_AGENTS_JOB_NAME="{job_name}"
60
+ export FIRE_AGENTS_PROMPT_FILE="{prompt_path}"
61
+ export FIRE_AGENTS_MATERIAL_FILE="{prompt_material_path}"
62
+ export FIRE_AGENTS_AGENT_LAUNCHER="{agent_cmd_launch_path}"
63
+
64
+ echo "Sleeping for {random_sleep_time:.2f} seconds to stagger agent startups..."
65
+ sleep {random_sleep_time:.2f}
66
+ echo "Launching agent {agent} with prompt from {prompt_path}"
67
+ echo "Launching agent {agent} with command from {agent_cmd_launch_path}"
68
+ echo "--------START OF AGENT OUTPUT--------"
69
+ sleep 0.1
32
70
 
33
- for idx, a_prompt in enumerate(prompts):
34
- prompt_path = tmp_dir / f"agent{idx}_prompt.txt"
35
- prompt_path.write_text(a_prompt, encoding="utf-8")
36
- cmd_path = tmp_dir / f"agent{idx}_cmd.sh"
71
+ """
37
72
  match agent:
38
73
  case "gemini":
39
- # model = "gemini-2.5-pro"
74
+ model = "gemini-2.5-pro"
40
75
  # model = "gemini-2.5-flash-lite"
41
- model = None # auto-select
76
+ # model = None # auto-select
42
77
  if model is None:
43
78
  model_arg = ""
44
79
  else:
@@ -51,8 +86,7 @@ def launch_agents(repo_root: Path, prompts: list[str], agent: AGENTS, *, max_age
51
86
  cmd = f"""
52
87
  export GEMINI_API_KEY={shlex.quote(api_key)}
53
88
  echo "Using Gemini API key $GEMINI_API_KEY"
54
- cat {prompt_path}
55
- GEMINI_API_KEY={shlex.quote(api_key)} bash -lc 'cat {safe_path} | gemini {model_arg} --yolo --prompt'
89
+ bash -lc 'cat {safe_path} | gemini {model_arg} --yolo --prompt'
56
90
  """
57
91
  case "cursor-agent":
58
92
  # As originally implemented
@@ -63,7 +97,6 @@ cursor-agent --print --output-format text < {prompt_path}
63
97
  """
64
98
  case "crush":
65
99
  cmd = f"""
66
- # cat {prompt_path} | crush run
67
100
  crush run {prompt_path}
68
101
  """
69
102
  case "q":
@@ -76,22 +109,34 @@ echo "Prepared prompt file at {prompt_path}"
76
109
  """
77
110
  case _:
78
111
  raise ValueError(f"Unsupported agent type: {agent}")
79
- random_sleep_time = random.uniform(0, 5)
80
- cmd_prefix = f"""
81
- echo "Sleeping for {random_sleep_time:.2f} seconds to stagger agent startups..."
82
- sleep {random_sleep_time:.2f}
83
- echo "Launching `{agent}` with prompt from {prompt_path}"
84
- echo "Launching `{agent}` with command from {cmd_path}"
85
- echo "--------START OF AGENT OUTPUT--------"
86
- sleep 0.1
87
- """
88
112
  cmd_postfix = """
89
113
  sleep 0.1
90
114
  echo "---------END OF AGENT OUTPUT---------"
91
115
  """
92
- cmd_path.write_text(cmd_prefix + cmd + cmd_postfix, encoding="utf-8")
93
- fire_cmd = f"bash {shlex.quote(str(cmd_path))}"
94
- tab_config.append(TabConfig(tabName=f"Agent{idx}", startDir=str(repo_root), command=fire_cmd))
116
+ agent_cmd_launch_path.write_text(cmd_prefix + cmd + cmd_postfix, encoding="utf-8")
117
+
95
118
 
96
- print(f"Launching a template with #{len(tab_config)} agents")
97
- return tab_config
119
+ # print(f"Launching a template with #{len(tab_config)} agents")
120
+ if len(all_materials_scripts) > 0:
121
+ all_materials_list_path = session_root / "all_materials_redistributed.txt"
122
+ all_materials_list_path.write_text("\n".join(str(p) for p in all_materials_scripts), encoding="utf-8")
123
+ print(f"All prompt materials listed @ {all_materials_list_path}")
124
+ return session_root
125
+
126
+
127
+ def get_agents_launch_layout(session_root: Path):
128
+ from machineconfig.utils.schemas.layouts.layout_types import TabConfig, LayoutConfig, LayoutsFile
129
+ tab_config: list[TabConfig] = []
130
+ prompt_root = session_root / "prompts"
131
+ all_dirs_under_prompts = [d for d in prompt_root.iterdir() if d.is_dir()]
132
+ launch_agents_squentially = ""
133
+ for a_prompt_dir in all_dirs_under_prompts:
134
+ idx = a_prompt_dir.name.split("_")[-1] # e.g., agent_0 -> 0
135
+ agent_cmd_path = a_prompt_dir / AGENT_NAME_FORMATTER.format(idx=idx)
136
+ fire_cmd = f"bash {shlex.quote(str(agent_cmd_path))}"
137
+ tab_config.append(TabConfig(tabName=f"Agent{idx}", startDir=str(session_root.parent.parent.parent), command=fire_cmd))
138
+ launch_agents_squentially += f". {shlex.quote(str(agent_cmd_path))}\n"
139
+ layout = LayoutConfig(layoutName="Agents", layoutTabs=tab_config)
140
+ (session_root / "launch_all_agents_sequentially.sh").write_text(launch_agents_squentially, encoding="utf-8")
141
+ layouts_file: LayoutsFile = LayoutsFile(version="1.0", layouts=[layout])
142
+ return layouts_file
@@ -1,6 +1,5 @@
1
- from machineconfig.utils.source_of_truth import EXCLUDE_DIRS
2
-
3
1
 
2
+ from machineconfig.utils.source_of_truth import EXCLUDE_DIRS
4
3
  import fnmatch
5
4
  from pathlib import Path
6
5
 
@@ -0,0 +1,52 @@
1
+
2
+
3
+ from typing import Literal, TypeAlias
4
+ from math import ceil
5
+ from pathlib import Path
6
+
7
+
8
+ SPLITTING_STRATEGY: TypeAlias = Literal[
9
+ "agent_cap", # User decides number of agents, rows/tasks determined automatically
10
+ "task_rows" # User decides number of rows/tasks, number of agents determined automatically
11
+ ]
12
+ DEFAULT_AGENT_CAP = 6
13
+
14
+
15
+ def chunk_prompts(prompt_material_path: Path, strategy: SPLITTING_STRATEGY, joiner: str, *, agent_cap: int | None, task_rows: int | None) -> list[str]:
16
+ """Chunk prompts based on splitting strategy.
17
+
18
+ Args:
19
+ prompts: List of prompts to chunk
20
+ strategy: Either 'agent_cap' or 'task_rows'
21
+ agent_cap: Maximum number of agents (used with 'agent_cap' strategy)
22
+ task_rows: Number of rows/tasks per agent (used with 'task_rows' strategy)
23
+ """
24
+ prompts = [p for p in prompt_material_path.read_text(encoding="utf-8", errors="ignore").split(joiner) if p.strip() != ""] # drop blank entries
25
+
26
+ if strategy == "agent_cap":
27
+ if agent_cap is None:
28
+ raise ValueError("agent_cap must be provided when using 'agent_cap' strategy")
29
+
30
+ if len(prompts) <= agent_cap:
31
+ return prompts
32
+
33
+ print(f"Chunking {len(prompts)} prompts into groups for up to {agent_cap} agents because it exceeds the cap.")
34
+ chunk_size = ceil(len(prompts) / agent_cap)
35
+ grouped: list[str] = []
36
+ for i in range(0, len(prompts), chunk_size):
37
+ grouped.append(joiner.join(prompts[i : i + chunk_size]))
38
+ return grouped
39
+
40
+ elif strategy == "task_rows":
41
+ if task_rows is None:
42
+ raise ValueError("task_rows must be provided when using 'task_rows' strategy")
43
+ if task_rows >= len(prompts):
44
+ return prompts
45
+ print(f"Chunking {len(prompts)} prompts into groups of {task_rows} rows/tasks each.")
46
+ grouped: list[str] = []
47
+ for i in range(0, len(prompts), task_rows):
48
+ grouped.append(joiner.join(prompts[i : i + task_rows]))
49
+ return grouped
50
+
51
+ else:
52
+ raise ValueError(f"Unknown splitting strategy: {strategy}")
@@ -21,6 +21,7 @@ from machineconfig.utils.utils2 import randstr, read_toml
21
21
  from machineconfig.scripts.python.fire_jobs_args_helper import get_args, FireJobArgs, extract_kwargs
22
22
  import platform
23
23
  from typing import Optional
24
+ from pathlib import Path
24
25
  # import os
25
26
 
26
27
 
@@ -42,7 +43,7 @@ def route(args: FireJobArgs) -> None:
42
43
  choice_file = PathExtended(choice_file)
43
44
  else:
44
45
  choice_file = path_obj
45
- repo_root = get_repo_root(str(choice_file))
46
+ repo_root = get_repo_root(Path(choice_file))
46
47
  print(f"💾 Selected file: {choice_file}.\nRepo root: {repo_root}")
47
48
  ve_root_from_file, ipy_profile = get_ve_path_and_ipython_profile(choice_file)
48
49
  if ipy_profile is None:
@@ -15,7 +15,6 @@ class RepositoryUpdateResult(TypedDict):
15
15
  commit_after: str
16
16
  commits_changed: bool
17
17
  pyproject_changed: bool
18
- uv_lock_changed: bool
19
18
  dependencies_changed: bool
20
19
  uv_sync_ran: bool
21
20
  uv_sync_success: bool
@@ -80,7 +79,6 @@ def update_repository(repo: git.Repo, auto_sync: bool, allow_password_prompt: bo
80
79
  "commit_after": "",
81
80
  "commits_changed": False,
82
81
  "pyproject_changed": False,
83
- "uv_lock_changed": False,
84
82
  "dependencies_changed": False,
85
83
  "uv_sync_ran": False,
86
84
  "uv_sync_success": False,
@@ -104,33 +102,18 @@ def update_repository(repo: git.Repo, auto_sync: bool, allow_password_prompt: bo
104
102
  result["uncommitted_files"] = changed_files
105
103
  print(f"⚠️ Repository has uncommitted changes: {', '.join(changed_files)}")
106
104
 
107
- # Check if the only change is uv.lock
108
- if len(changed_files) == 1 and changed_files[0] == "uv.lock":
109
- print("🔒 Only uv.lock has changes, resetting it...")
110
- try:
111
- # Reset uv.lock file
112
- subprocess.run(["git", "checkout", "HEAD", "--", "uv.lock"], cwd=repo_path, check=True)
113
- print("✅ uv.lock has been reset")
114
- except subprocess.CalledProcessError as e:
115
- result["status"] = "error"
116
- result["error_message"] = f"Failed to reset uv.lock: {e}"
117
- print(f"❌ Failed to reset uv.lock: {e}")
118
- return result
119
- else:
120
- # Multiple files or files other than uv.lock have changes
121
- result["status"] = "error"
122
- result["error_message"] = f"Cannot update repository - there are pending changes in: {', '.join(changed_files)}. Please commit or stash your changes first."
123
- raise RuntimeError(result["error_message"])
105
+ # Repository has uncommitted changes - cannot update
106
+ result["status"] = "error"
107
+ result["error_message"] = f"Cannot update repository - there are pending changes in: {', '.join(changed_files)}. Please commit or stash your changes first."
108
+ raise RuntimeError(result["error_message"])
124
109
  else:
125
110
  print("✅ Repository is clean")
126
111
 
127
- # Check if this repo has pyproject.toml or uv.lock
112
+ # Check if this repo has pyproject.toml
128
113
  pyproject_path = repo_path / "pyproject.toml"
129
- uv_lock_path = repo_path / "uv.lock"
130
114
 
131
115
  # Get hashes before pull
132
116
  pyproject_hash_before = get_file_hash(pyproject_path)
133
- uv_lock_hash_before = get_file_hash(uv_lock_path)
134
117
 
135
118
  # Get current commit hash before pull
136
119
  result["commit_before"] = repo.head.commit.hexsha
@@ -239,20 +222,14 @@ def update_repository(repo: git.Repo, auto_sync: bool, allow_password_prompt: bo
239
222
  print(f"⚠️ Failed to pull from {remote.name}: {e}")
240
223
  continue
241
224
 
242
- # Check if pyproject.toml or uv.lock changed after pull
225
+ # Check if pyproject.toml changed after pull
243
226
  pyproject_hash_after = get_file_hash(pyproject_path)
244
- uv_lock_hash_after = get_file_hash(uv_lock_path)
245
227
 
246
228
  if pyproject_hash_before != pyproject_hash_after:
247
229
  print("📋 pyproject.toml has changed")
248
230
  result["pyproject_changed"] = True
249
231
  result["dependencies_changed"] = True
250
232
 
251
- if uv_lock_hash_before != uv_lock_hash_after:
252
- print("🔒 uv.lock has changed")
253
- result["uv_lock_changed"] = True
254
- result["dependencies_changed"] = True
255
-
256
233
  # Special handling for machineconfig repository
257
234
  if result["is_machineconfig_repo"]:
258
235
  print("🛠 Special handling for machineconfig repository...")
@@ -0,0 +1,70 @@
1
+ """TypedDict definitions for fire_agents.py inputs.
2
+
3
+ This module defines the structured input types for the fire_agents main function,
4
+ capturing all user inputs collected during interactive execution.
5
+ """
6
+
7
+ from pathlib import Path
8
+ from typing import TypedDict, Literal, NotRequired
9
+ from machineconfig.scripts.python.fire_agents_load_balancer import SPLITTING_STRATEGY
10
+ from machineconfig.scripts.python.fire_agents_help_launch import AGENTS
11
+
12
+ SEARCH_STRATEGIES = Literal["file_path", "keyword_search", "filename_pattern"]
13
+
14
+
15
+ class FilePathSearchInput(TypedDict):
16
+ """Input for file_path search strategy."""
17
+ file_path: str
18
+ separator: str # Default: "\n"
19
+
20
+
21
+ class KeywordSearchInput(TypedDict):
22
+ """Input for keyword_search strategy."""
23
+ keyword: str
24
+
25
+
26
+ class FilenamePatternSearchInput(TypedDict):
27
+ """Input for filename_pattern search strategy."""
28
+ pattern: str # e.g., '*.py', '*test*', 'config.*'
29
+
30
+
31
+ class AgentCapSplittingInput(TypedDict):
32
+ """Input for agent_cap splitting strategy."""
33
+ agent_cap: int # Default: 6
34
+
35
+
36
+ class TaskRowsSplittingInput(TypedDict):
37
+ """Input for task_rows splitting strategy."""
38
+ task_rows: int # Default: 13
39
+
40
+
41
+ class FireAgentsMainInput(TypedDict):
42
+ """Complete input structure for fire_agents main function."""
43
+
44
+ # Core configuration
45
+ repo_root: Path
46
+ search_strategy: SEARCH_STRATEGIES
47
+ splitting_strategy: SPLITTING_STRATEGY
48
+ agent_selected: AGENTS
49
+ prompt_prefix: str
50
+ job_name: str # Default: "AI_Agents"
51
+ keep_material_in_separate_file: bool # Default: False
52
+ max_agents: int # Default: 25
53
+
54
+ # Search strategy specific inputs (only one will be present based on search_strategy)
55
+ file_path_input: NotRequired[FilePathSearchInput]
56
+ keyword_search_input: NotRequired[KeywordSearchInput]
57
+ filename_pattern_input: NotRequired[FilenamePatternSearchInput]
58
+
59
+ # Splitting strategy specific inputs (only one will be present based on splitting_strategy)
60
+ agent_cap_input: NotRequired[AgentCapSplittingInput]
61
+ task_rows_input: NotRequired[TaskRowsSplittingInput]
62
+
63
+
64
+ class FireAgentsRuntimeData(TypedDict):
65
+ """Runtime data derived from inputs during execution."""
66
+
67
+ prompt_material: str
68
+ separator: str
69
+ prompt_material_re_splitted: list[str]
70
+ random_name: str # 3-character random string for session naming
@@ -17,7 +17,6 @@ class TabConfig(TypedDict):
17
17
 
18
18
  class LayoutConfig(TypedDict):
19
19
  """Configuration for a complete layout with its tabs."""
20
-
21
20
  layoutName: str
22
21
  layoutTabs: List[TabConfig]
23
22
 
machineconfig/utils/ve.py CHANGED
@@ -1,7 +1,9 @@
1
+
1
2
  from machineconfig.utils.path_reduced import PathExtended as PathExtended
2
3
  from machineconfig.utils.utils2 import read_ini
3
4
  import platform
4
5
  from typing import Optional
6
+ from pathlib import Path
5
7
 
6
8
 
7
9
  def get_ve_path_and_ipython_profile(init_path: PathExtended) -> tuple[Optional[str], Optional[str]]:
@@ -38,15 +40,17 @@ def get_ve_path_and_ipython_profile(init_path: PathExtended) -> tuple[Optional[s
38
40
  return ve_path, ipy_profile
39
41
 
40
42
 
41
- def get_repo_root(choice_file: str) -> Optional[str]:
43
+ def get_repo_root(path: Path) -> Optional[Path]:
42
44
  from git import Repo, InvalidGitRepositoryError
43
45
 
44
46
  try:
45
- repo = Repo(PathExtended(choice_file), search_parent_directories=True)
46
- repo_root = str(repo.working_tree_dir) if repo.working_tree_dir else None
47
+ repo = Repo(str(path), search_parent_directories=True)
48
+ root = repo.working_tree_dir
49
+ if root is not None:
50
+ return Path(root)
47
51
  except InvalidGitRepositoryError:
48
- repo_root = None
49
- return repo_root
52
+ pass
53
+ return None
50
54
 
51
55
 
52
56
  def get_ve_activate_line(ve_root: str):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: machineconfig
3
- Version: 2.9
3
+ Version: 2.94
4
4
  Summary: Dotfiles management package
5
5
  Author-email: Alex Al-Saffar <programmer@usa.com>
6
6
  License: Apache 2.0
@@ -17,7 +17,7 @@ machineconfig/cluster/sessions_managers/wt_local_manager.py,sha256=EqSdzHx5VLkax
17
17
  machineconfig/cluster/sessions_managers/wt_remote.py,sha256=7KT5D0FM9wwY8N3Mp9uO9rv3z29n6HIDJfPSLWXOIIs,8755
18
18
  machineconfig/cluster/sessions_managers/wt_remote_manager.py,sha256=V7Z_1sLGfCjyGh2rxbtnDCzrzlMYrdY_6uG3Gljp20o,19758
19
19
  machineconfig/cluster/sessions_managers/zellij_local.py,sha256=I2iQAS7G13lxQa0KCfHHXYMo9_54dDUwOYNKW_AWlek,17081
20
- machineconfig/cluster/sessions_managers/zellij_local_manager.py,sha256=oWqhX8-hfXYptaqcAlIo1_ZMFRr2LqXKMu1njYrLftM,24233
20
+ machineconfig/cluster/sessions_managers/zellij_local_manager.py,sha256=eemuygUYs6jjAns_xjX-W25T8pVw4Ds7aXK8WQQ2Ot0,23464
21
21
  machineconfig/cluster/sessions_managers/zellij_remote.py,sha256=G1SdycIHuCBMOnO5kfxDNN-boNxZ6PSh0xsZsDK8H1s,11039
22
22
  machineconfig/cluster/sessions_managers/zellij_remote_manager.py,sha256=2ZoSPrlsYJbugH8f8doNIDhmbv2M61DN9KIQNVUqE1k,8116
23
23
  machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py,sha256=CFGcZPFTZQJtFf0OvMUHhadZ0qbImCP3wxvbWYVcVYo,7445
@@ -170,12 +170,13 @@ machineconfig/scripts/python/devops_add_identity.py,sha256=mRoH2_SD_i4xzhBSuXk97
170
170
  machineconfig/scripts/python/devops_add_ssh_key.py,sha256=zdVlHtD6vlpXMLuwIqN5nHcm023ty2qVArWe7zPxgCo,6949
171
171
  machineconfig/scripts/python/devops_backup_retrieve.py,sha256=I7lghN_HJT1mPaZ7zcD1jIJnTjNOvUC7nQZAoySPp7o,5563
172
172
  machineconfig/scripts/python/devops_devapps_install.py,sha256=xQaOHlr6ziHxrx7e5Ri7WQ-FjIW0hjzDhlPJC-3qwks,8596
173
- machineconfig/scripts/python/devops_update_repos.py,sha256=dIvHfq9zRkZhvFjYXOamJEAuF-AWXwtSnwGLOf2SE7E,8350
173
+ machineconfig/scripts/python/devops_update_repos.py,sha256=sqtkpQ2InUz-TFLhejN8EZqjZC1aBanAhohL6aHxPf0,8213
174
174
  machineconfig/scripts/python/dotfile.py,sha256=miL8mQH2AqPdnHSz0Cxa7qQavaOmzTD9DAF66H2PRzA,2259
175
- machineconfig/scripts/python/fire_agents.py,sha256=RFUZ-bULx93a3MkjSl-2rGlx_bguQUcGqoTkefuUN_I,8739
176
- machineconfig/scripts/python/fire_agents_help_launch.py,sha256=41GagLs0QJCdoS-5aVcm4EpApRKzAXdc_SFFUTdSbIQ,3746
177
- machineconfig/scripts/python/fire_agents_help_search.py,sha256=EmvCh9HD45sePgrE1skvd3CZsOVh13AC7S0PNeMP__0,2992
178
- machineconfig/scripts/python/fire_jobs.py,sha256=FvfJ-LrTUTVJRVT7i1LeP2YEUYH21RkYwKlVoEJxZNs,16616
175
+ machineconfig/scripts/python/fire_agents.py,sha256=XIxOhI5q7DWpZJMs3rrhvdl0k8t-UICEpSp5W8Oc7bk,7716
176
+ machineconfig/scripts/python/fire_agents_help_launch.py,sha256=70iFF0wmFYKvaPsUy35_bIw5ivn5EdonahjSw1GTMIs,6125
177
+ machineconfig/scripts/python/fire_agents_help_search.py,sha256=jEgPFMHL4s1VHQ1AJO1YdV2pyGdxdaCY5XxDUvC55nU,2991
178
+ machineconfig/scripts/python/fire_agents_load_balancer.py,sha256=FeTfbp7n6jHHwMp4L6KVYO5HgenP57XpCyaab_vCwc0,2135
179
+ machineconfig/scripts/python/fire_jobs.py,sha256=IRs0_KY8WvYdF4Zr9fF-wWyqZYTx-zfhAx5ExYbwpOg,16642
179
180
  machineconfig/scripts/python/fire_jobs_args_helper.py,sha256=TfCKSExGZhYrZ6JmXIHsd0wpNSWcKeLeRh9gFR3FG-M,4330
180
181
  machineconfig/scripts/python/fire_jobs_layout_helper.py,sha256=_cx6ZnWezes-s13HXyKiI16vj9gF-Mpo_OF4StDQfFs,3222
181
182
  machineconfig/scripts/python/fire_jobs_streamlit_helper.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -189,7 +190,7 @@ machineconfig/scripts/python/onetimeshare.py,sha256=bmGsNnskym5OWfIhpOfZG5jq3m89
189
190
  machineconfig/scripts/python/pomodoro.py,sha256=SPkfeoZGv8rylGiOyzQ7UK3aXZ3G2FIOuGkSuBUggOI,2019
190
191
  machineconfig/scripts/python/repos.py,sha256=nV5iJV7BLwBKiS_aqnrh63cfp-3k7nZyfUGtspwjKsM,7229
191
192
  machineconfig/scripts/python/repos_helper_record.py,sha256=rtnjcr2ZCzt4j7oSPws25fPiCzshOEU_jH1ORW4QWx0,11372
192
- machineconfig/scripts/python/repos_helper_update.py,sha256=yEXG1eFbeZPCDOTKA3_X9TpHa-WzbZcUFTlXAlT-itQ,12160
193
+ machineconfig/scripts/python/repos_helper_update.py,sha256=ypRFYSjbVe-VHicR9kpO9eBvgW1kmBvUA21FnczUXPw,11040
193
194
  machineconfig/scripts/python/scheduler.py,sha256=7IBjMMOHMkklcWzYwz93EH9XzbJ5uPqU03bJ_lYbRNo,3083
194
195
  machineconfig/scripts/python/snapshot.py,sha256=aDvKeoniZaeTSNv9zWBUajaj2yagAxVdfuvO1_tgq5Y,1026
195
196
  machineconfig/scripts/python/start_slidev.py,sha256=MPCN0JgRzOAXywj6n9s0iZYcLFAscP-eOPQvkUyElRA,4525
@@ -202,14 +203,15 @@ machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc,sha256=IppPGJ6
202
203
  machineconfig/scripts/python/__pycache__/cloud_repo_sync.cpython-313.pyc,sha256=sWCevZgeQxIHoQ5Ea7a0OpjgJpkcKMoZylwCyizXYqA,10555
203
204
  machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc,sha256=Rs6A-GpFrTXuXAn0tO3cGvilciJMHk0ujt_JPlsxaJ4,8216
204
205
  machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-313.pyc,sha256=BrMh1Rzca_iY0EvlPsSXBqpwOZw-De6I3y8blXJZCuc,8616
205
- machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc,sha256=6gTDTRboQbrSfF-8gR7SSJZSqygd6yNf_08xnxZYLno,23284
206
+ machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc,sha256=M9trjuribG312W-mwFaY7GJqlDf0tEGC1UzqWFiRBKc,11054
206
207
  machineconfig/scripts/python/__pycache__/fire_agents.cpython-313.pyc,sha256=61OZTwltoquxWzQPwHue4SehHuiaDExcl57aN07Cpww,15829
207
208
  machineconfig/scripts/python/__pycache__/get_zellij_cmd.cpython-313.pyc,sha256=jx8_Gyx1kkqCwIe8W51h9hO89yuzTdd4Vh5oPRBOLio,826
208
209
  machineconfig/scripts/python/__pycache__/repos.cpython-313.pyc,sha256=CoqJX-Oc-7suZ03dIkNqWscKXsuiswHHoGPM_VaZzS8,9642
209
210
  machineconfig/scripts/python/__pycache__/repos_helper_record.cpython-313.pyc,sha256=VWsAX_sFJxVD0MUiZ1d9Bj9p4kw5gquukGLoJlID-lQ,13573
211
+ machineconfig/scripts/python/__pycache__/repos_helper_update.cpython-313.pyc,sha256=bo6zyGOBgrOVDJx7MGdGQ6pF00xXqa1UyCDcwtWNAc4,11852
210
212
  machineconfig/scripts/python/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
211
213
  machineconfig/scripts/python/ai/generate_files.py,sha256=Vfjgd0skJu-WTgqUxmOVFzaNMfSFBaFmY5oGGVY7MZY,2860
212
- machineconfig/scripts/python/ai/mcinit.py,sha256=8A0vJYJbw4CJjd4pIS5rL8uYbewn0ZmlTRiETLTSGMY,5749
214
+ machineconfig/scripts/python/ai/mcinit.py,sha256=dQKrnrH-HzT7bIyOpio2ikh02ierrpBM0JHm3dGT9wM,5437
213
215
  machineconfig/scripts/python/ai/chatmodes/Thinking-Beast-Mode.chatmode.md,sha256=Tu-fWxX_FLiIBRdgOndMhewK41kIHDoYxuGZ1kF6dYA,17947
214
216
  machineconfig/scripts/python/ai/chatmodes/Ultimate-Transparent-Thinking-Beast-Mode.chatmode.md,sha256=kB8u_QAZar9StuywXcbPTUCbAVHKin0s0brssOroK5o,29019
215
217
  machineconfig/scripts/python/ai/chatmodes/deepResearch.chatmode.md,sha256=WRbZXkdOPw5pVAFjR51n9IRTtqw3TE7jUt4BNyN5Z8k,5165
@@ -406,7 +408,7 @@ machineconfig/utils/terminal.py,sha256=k3xoMwJPGvmaeL9CxEIwrcQjRNN6EnJItoIIxXrUY
406
408
  machineconfig/utils/upgrade_packages.py,sha256=H96zVJEWXJW07nh5vhjuSCrPtXGqoUb7xeJsFYYdmCI,3330
407
409
  machineconfig/utils/utils2.py,sha256=Y9bX4gaaPih7gbOeTcfPhQ3CcMFKGXgVCEQhVhljH4Q,2526
408
410
  machineconfig/utils/utils5.py,sha256=s2NFUBcm4Jeuet8sUC7WKGSrYT4BnqTog39bsynXLik,15166
409
- machineconfig/utils/ve.py,sha256=2-XzS2LV1c-iW3SZ3elELrCeAisXLNLqQU4Ayieq6Ho,2763
411
+ machineconfig/utils/ve.py,sha256=bi2bCiBbCdNICvXA8stXvMOmvtipd1dMePDTX_IYZcc,2765
410
412
  machineconfig/utils/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
411
413
  machineconfig/utils/ai/generate_file_checklist.py,sha256=HVPSKM5ko17e_x1rHZ8XATaUKUVo4gtlSYGKbGd7Mh0,2834
412
414
  machineconfig/utils/cloud/onedrive/setup_oauth.py,sha256=ZTVkqgrwbV_EoPvyT8dyOTUE0ur3BW4sa9o6QYtt5Bo,2341
@@ -414,10 +416,11 @@ machineconfig/utils/cloud/onedrive/transaction.py,sha256=m-aNcnWj_gfZVvJOSpkdIqj
414
416
  machineconfig/utils/installer_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
415
417
  machineconfig/utils/installer_utils/installer_abc.py,sha256=jR5XvrCjisIzoxk2VL0VhoVv-PkKdstDHGLcYky5Mqs,5319
416
418
  machineconfig/utils/installer_utils/installer_class.py,sha256=3jknokPmaN4LuBR5n6LrewIYo4-PrTuhYnFJIuhjjuY,20342
417
- machineconfig/utils/schemas/layouts/layout_types.py,sha256=SPgoc0ncazKlXtQUhi7u1mMP2JvDt5jSG9YQmBr7-zc,616
419
+ machineconfig/utils/schemas/fire_agents/fire_agents_input.py,sha256=Pkp5nzmzuFOKhVHhaAcue7gmCOKrm7m0ft_FSdDn6X8,2350
420
+ machineconfig/utils/schemas/layouts/layout_types.py,sha256=9l8c02MVX_e-OOvGTlfM_Ef6wWw6_Wqru1g7HxWnhgU,615
418
421
  machineconfig/utils/schemas/repos/repos_types.py,sha256=beWlwPRNDBABmlzxHBYkAsMXS1MgFFdDZf4G2ge8J-I,408
419
- machineconfig-2.9.dist-info/METADATA,sha256=7SpBqWO0al_Ce8zqCT2AyKdpFzT5bJ_7QvD_nMoK0qQ,7048
420
- machineconfig-2.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
421
- machineconfig-2.9.dist-info/entry_points.txt,sha256=71EzS7_2LTIigVxC1YXNxHXhC9mu5Me2Feyq2KocXBI,977
422
- machineconfig-2.9.dist-info/top_level.txt,sha256=porRtB8qms8fOIUJgK-tO83_FeH6Bpe12oUVC670teA,14
423
- machineconfig-2.9.dist-info/RECORD,,
422
+ machineconfig-2.94.dist-info/METADATA,sha256=g73zifTyWLfyZujy1aydreg3ntB3wCrENUSpSjUpCh0,7049
423
+ machineconfig-2.94.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
424
+ machineconfig-2.94.dist-info/entry_points.txt,sha256=71EzS7_2LTIigVxC1YXNxHXhC9mu5Me2Feyq2KocXBI,977
425
+ machineconfig-2.94.dist-info/top_level.txt,sha256=porRtB8qms8fOIUJgK-tO83_FeH6Bpe12oUVC670teA,14
426
+ machineconfig-2.94.dist-info/RECORD,,