machineconfig 5.26__py3-none-any.whl → 5.28__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.
- machineconfig/cluster/sessions_managers/zellij_local_manager.py +1 -0
- machineconfig/jobs/installer/installer_data.json +17 -0
- machineconfig/jobs/installer/linux_scripts/brave.sh +4 -14
- machineconfig/jobs/installer/linux_scripts/docker.sh +5 -17
- machineconfig/jobs/installer/linux_scripts/docker_start.sh +6 -14
- machineconfig/jobs/installer/linux_scripts/edge.sh +3 -11
- machineconfig/jobs/installer/linux_scripts/nerdfont.sh +5 -17
- machineconfig/jobs/installer/linux_scripts/pgsql.sh +3 -11
- machineconfig/jobs/installer/linux_scripts/redis.sh +5 -17
- machineconfig/jobs/installer/linux_scripts/timescaledb.sh +6 -20
- machineconfig/jobs/installer/linux_scripts/vscode.sh +5 -17
- machineconfig/jobs/installer/linux_scripts/warp-cli.sh +5 -17
- machineconfig/jobs/installer/linux_scripts/wezterm.sh +3 -11
- machineconfig/jobs/installer/package_groups.py +70 -111
- machineconfig/jobs/linux/msc/lid.sh +2 -8
- machineconfig/jobs/linux/msc/network.sh +2 -8
- machineconfig/scripts/cloud/init.sh +6 -20
- machineconfig/scripts/linux/share_cloud.sh +11 -25
- machineconfig/scripts/python/agents.py +22 -31
- machineconfig/scripts/python/cloud_repo_sync.py +14 -29
- machineconfig/scripts/python/devops.py +7 -10
- machineconfig/scripts/python/helpers/repo_sync_helpers.py +1 -1
- machineconfig/scripts/python/helpers_fire/fire_agents_help_launch.py +30 -48
- machineconfig/scripts/python/helpers_fire/fire_agents_helper_types.py +24 -6
- machineconfig/scripts/python/helpers_fire/fire_crush.json +14 -0
- machineconfig/scripts/python/helpers_fire/fire_crush.py +37 -0
- machineconfig/scripts/python/helpers_fire/fire_cursor_agents.py +23 -0
- machineconfig/scripts/python/helpers_fire/fire_gemini.py +41 -0
- machineconfig/scripts/python/helpers_fire/fire_q.py +19 -0
- machineconfig/scripts/python/helpers_fire/prompt.txt +2 -0
- machineconfig/scripts/python/helpers_fire/template.ps1 +0 -0
- machineconfig/scripts/python/helpers_fire/template.sh +31 -0
- machineconfig/scripts/python/interactive.py +21 -19
- machineconfig/scripts/python/repos.py +4 -1
- machineconfig/scripts/python/secure_repo.py +15 -0
- machineconfig/settings/broot/br.sh +0 -4
- machineconfig/setup_linux/__init__.py +2 -2
- machineconfig/setup_linux/apps.sh +7 -9
- machineconfig/setup_linux/apps_desktop.sh +11 -35
- machineconfig/setup_linux/apps_gui.sh +4 -14
- machineconfig/setup_linux/nix/cli_installation.sh +9 -29
- machineconfig/setup_linux/web_shortcuts/interactive.sh +1 -1
- machineconfig/setup_windows/__init__.py +2 -2
- machineconfig/utils/code.py +3 -3
- machineconfig/utils/files/read.py +1 -1
- machineconfig/utils/installer.py +15 -21
- machineconfig/utils/installer_utils/installer.py +3 -4
- machineconfig/utils/installer_utils/installer_abc.py +4 -4
- machineconfig/utils/installer_utils/installer_class.py +11 -46
- machineconfig/utils/io.py +0 -1
- {machineconfig-5.26.dist-info → machineconfig-5.28.dist-info}/METADATA +3 -3
- {machineconfig-5.26.dist-info → machineconfig-5.28.dist-info}/RECORD +59 -52
- {machineconfig-5.26.dist-info → machineconfig-5.28.dist-info}/entry_points.txt +0 -1
- machineconfig/scripts/linux/cloud_repo_sync +0 -2
- machineconfig/scripts/windows/cloud_repo_sync.ps1 +0 -1
- /machineconfig/setup_linux/{repos.sh → machineconfig.sh} +0 -0
- /machineconfig/setup_linux/{ve.sh → uv.sh} +0 -0
- /machineconfig/setup_windows/{repos.ps1 → machineconfig.ps1} +0 -0
- /machineconfig/setup_windows/{ve.ps1 → uv.ps1} +0 -0
- {machineconfig-5.26.dist-info → machineconfig-5.28.dist-info}/WHEEL +0 -0
- {machineconfig-5.26.dist-info → machineconfig-5.28.dist-info}/top_level.txt +0 -0
|
@@ -3,31 +3,31 @@ from rich.console import Console
|
|
|
3
3
|
from rich.panel import Panel
|
|
4
4
|
import typer
|
|
5
5
|
|
|
6
|
-
from machineconfig.utils.io import read_ini
|
|
7
6
|
from machineconfig.utils.path_extended import PathExtended
|
|
8
7
|
from machineconfig.utils.terminal import Response
|
|
9
8
|
from machineconfig.utils.source_of_truth import CONFIG_PATH, DEFAULTS_PATH
|
|
10
|
-
from machineconfig.utils.options import choose_from_options
|
|
11
9
|
from machineconfig.utils.code import get_shell_file_executing_python_script, write_shell_script_to_file
|
|
12
10
|
|
|
13
11
|
import platform
|
|
14
12
|
import subprocess
|
|
15
13
|
from typing import Optional, Literal
|
|
16
14
|
from pathlib import Path
|
|
17
|
-
|
|
15
|
+
|
|
18
16
|
|
|
19
17
|
console = Console()
|
|
20
18
|
|
|
21
19
|
|
|
22
20
|
def main(
|
|
23
21
|
cloud: Optional[str] = typer.Option(None, "--cloud", "-c", help="Cloud storage profile name. If not provided, uses default from config."),
|
|
24
|
-
|
|
22
|
+
repo: Optional[str] = typer.Option(None, "--repo", "-r", help="Path to the local repository. Defaults to current working directory."),
|
|
25
23
|
message: Optional[str] = typer.Option(None, "--message", "-m", help="Commit message for local changes."),
|
|
26
24
|
on_conflict: Literal["ask", "pushLocalMerge", "overwriteLocal", "InspectRepos", "RemoveLocalRclone"] = typer.Option("ask", "--on-conflict", "-oc", help="Action to take on merge conflict. Default is 'ask'."),
|
|
27
25
|
pwd: Optional[str] = typer.Option(None, "--password", help="Password for encryption/decryption of the remote repository."),
|
|
28
26
|
):
|
|
29
27
|
if cloud is None:
|
|
30
28
|
try:
|
|
29
|
+
from machineconfig.utils.io import read_ini
|
|
30
|
+
|
|
31
31
|
cloud_resolved = read_ini(DEFAULTS_PATH)["general"]["rclone_config_name"]
|
|
32
32
|
console.print(Panel(f"⚠️ Using default cloud: `{cloud_resolved}` from {DEFAULTS_PATH}", title="Default Cloud", border_style="yellow"))
|
|
33
33
|
except FileNotFoundError:
|
|
@@ -35,7 +35,7 @@ def main(
|
|
|
35
35
|
return ""
|
|
36
36
|
else:
|
|
37
37
|
cloud_resolved = cloud
|
|
38
|
-
repo_local_root = PathExtended.cwd() if
|
|
38
|
+
repo_local_root = PathExtended.cwd() if repo is None else PathExtended(repo).expanduser().absolute()
|
|
39
39
|
repo_local_obj = git.Repo(repo_local_root, search_parent_directories=True)
|
|
40
40
|
repo_local_root = PathExtended(repo_local_obj.working_dir) # cwd might have been in a sub directory of repo_root, so its better to redefine it.
|
|
41
41
|
PathExtended(CONFIG_PATH).joinpath("remote").mkdir(parents=True, exist_ok=True)
|
|
@@ -139,15 +139,17 @@ git commit -am "finished merging"
|
|
|
139
139
|
|
|
140
140
|
console.print(Panel("🔄 RESOLVE MERGE CONFLICT\nChoose an option to resolve the conflict:", title_align="left", border_style="blue"))
|
|
141
141
|
|
|
142
|
-
print(f"•
|
|
143
|
-
print(f"•
|
|
144
|
-
print(f"•
|
|
145
|
-
print(f"•
|
|
142
|
+
print(f"• {option1:75} 👉 {shell_file_1}")
|
|
143
|
+
print(f"• {option2:75} 👉 {shell_file_2}")
|
|
144
|
+
print(f"• {option3:75} 👉 {shell_file_3}")
|
|
145
|
+
print(f"• {option4:75} 👉 {shell_file_4}")
|
|
146
|
+
print("\n\n")
|
|
146
147
|
|
|
147
148
|
program_content = None
|
|
148
149
|
match on_conflict:
|
|
149
150
|
case "ask":
|
|
150
|
-
|
|
151
|
+
import questionary
|
|
152
|
+
choice = questionary.select("Choose one option:", choices=[option1, option2, option3, option4]).ask()
|
|
151
153
|
if choice == option1:
|
|
152
154
|
program_content = shell_file_1.read_text(encoding="utf-8")
|
|
153
155
|
elif choice == option2:
|
|
@@ -168,24 +170,7 @@ git commit -am "finished merging"
|
|
|
168
170
|
program_content = program_4
|
|
169
171
|
case _:
|
|
170
172
|
raise ValueError(f"Unknown action: {on_conflict}")
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
173
|
+
from machineconfig.utils.code import run_shell_script
|
|
174
|
+
run_shell_script(script=program_content)
|
|
174
175
|
return program_content
|
|
175
176
|
|
|
176
|
-
|
|
177
|
-
def args_parser():
|
|
178
|
-
# Check if no arguments provided (excluding the script name)
|
|
179
|
-
if len(sys.argv) == 1:
|
|
180
|
-
app = typer.Typer(add_completion=False, help="Sync a local git repository with a remote encrypted cloud copy.")
|
|
181
|
-
app.command()(main)
|
|
182
|
-
app(["--help"])
|
|
183
|
-
return
|
|
184
|
-
|
|
185
|
-
app = typer.Typer(add_completion=False, no_args_is_help=True, help="Sync a local git repository with a remote encrypted cloud copy.")
|
|
186
|
-
app.command()(main)
|
|
187
|
-
app()
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
if __name__ == "__main__":
|
|
191
|
-
args_parser()
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
"""devops with emojis"""
|
|
2
2
|
|
|
3
3
|
import typer
|
|
4
|
-
from typing import Literal, Annotated, Optional
|
|
4
|
+
from typing import Literal, Annotated, Optional
|
|
5
5
|
|
|
6
|
-
import machineconfig.scripts.python.share_terminal as share_terminal
|
|
7
6
|
import machineconfig.scripts.python.repos as repos
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
import machineconfig.scripts.python.share_terminal as share_terminal
|
|
10
8
|
|
|
11
9
|
app = typer.Typer(help="🛠️ DevOps operations", no_args_is_help=True)
|
|
12
10
|
@app.command(no_args_is_help=True)
|
|
13
11
|
def install( which: Optional[str] = typer.Option(None, "--which", "-w", help="Comma-separated list of program names to install."),
|
|
14
|
-
group: Optional[
|
|
12
|
+
group: Optional[str] = typer.Option(None, "--group", "-g", help="Groups names. A group is bundle of apps. See available groups when running interactively."),
|
|
15
13
|
interactive: bool = typer.Option(False, "--interactive", "-ia", help="Interactive selection of programs to install."),
|
|
16
14
|
) -> None:
|
|
17
15
|
"""📦 Install essential packages"""
|
|
@@ -30,7 +28,6 @@ app.add_typer(nw_apps, name="network")
|
|
|
30
28
|
self_app = typer.Typer(help="🔄 SELF operations subcommands", no_args_is_help=True)
|
|
31
29
|
app.add_typer(self_app, name="self")
|
|
32
30
|
|
|
33
|
-
|
|
34
31
|
@self_app.command()
|
|
35
32
|
def update():
|
|
36
33
|
"""🔄 UPDATE essential repos"""
|
|
@@ -53,12 +50,12 @@ def clone():
|
|
|
53
50
|
from machineconfig.utils.code import run_shell_script
|
|
54
51
|
from machineconfig.profile.shell import create_default_shell_profile
|
|
55
52
|
if platform.system() == "Windows":
|
|
56
|
-
from machineconfig.setup_windows import
|
|
53
|
+
from machineconfig.setup_windows import MACHINECONFIG
|
|
57
54
|
create_default_shell_profile(method="copy")
|
|
58
55
|
else:
|
|
59
|
-
from machineconfig.setup_linux import
|
|
56
|
+
from machineconfig.setup_linux import MACHINECONFIG
|
|
60
57
|
create_default_shell_profile(method="reference")
|
|
61
|
-
run_shell_script(
|
|
58
|
+
run_shell_script(MACHINECONFIG.read_text(encoding="utf-8"))
|
|
62
59
|
|
|
63
60
|
|
|
64
61
|
|
|
@@ -125,7 +122,7 @@ def setup():
|
|
|
125
122
|
else:
|
|
126
123
|
raise NotImplementedError(f"Platform {platform.system()} is not supported.")
|
|
127
124
|
from machineconfig.utils.code import run_shell_script
|
|
128
|
-
run_shell_script(
|
|
125
|
+
run_shell_script(script=program)
|
|
129
126
|
|
|
130
127
|
|
|
131
128
|
@app_data.command()
|
|
@@ -82,5 +82,5 @@ def check_dotfiles_version_is_beyond(commit_dtm: str, update: bool) -> bool:
|
|
|
82
82
|
console.print(Panel(f"🔄 UPDATE REQUIRED | Updating dotfiles because {dtm} < {datetime.fromisoformat(commit_dtm)}", border_style="bold blue", expand=False))
|
|
83
83
|
from machineconfig.scripts.python.cloud_repo_sync import main
|
|
84
84
|
|
|
85
|
-
main(cloud=None,
|
|
85
|
+
main(cloud=None, repo=dotfiles_path)
|
|
86
86
|
return res
|
|
@@ -2,13 +2,12 @@
|
|
|
2
2
|
import random
|
|
3
3
|
import shlex
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from machineconfig.scripts.python.helpers_fire.fire_agents_helper_types import AGENTS, AGENT_NAME_FORMATTER
|
|
5
|
+
from machineconfig.scripts.python.helpers_fire.fire_agents_helper_types import AGENTS, AGENT_NAME_FORMATTER, MATCHINE, PROVIDER, MODEL
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
def
|
|
8
|
+
def get_api_keys(provider: PROVIDER) -> list[str]:
|
|
9
9
|
from machineconfig.utils.io import read_ini
|
|
10
|
-
|
|
11
|
-
config = read_ini(Path.home().joinpath("dotfiles/creds/llm/gemini/api_keys.ini"))
|
|
10
|
+
config = read_ini(Path.home().joinpath(f"dotfiles/creds/llm/{provider}/api_keys.ini"))
|
|
12
11
|
res: list[str] = []
|
|
13
12
|
for a_section_name in list(config.sections()):
|
|
14
13
|
a_section = config[a_section_name]
|
|
@@ -16,11 +15,12 @@ def get_gemini_api_keys() -> list[str]:
|
|
|
16
15
|
api_key = a_section["api_key"].strip()
|
|
17
16
|
if api_key:
|
|
18
17
|
res.append(api_key)
|
|
19
|
-
print(f"Found {len(res)}
|
|
18
|
+
print(f"Found {len(res)} {provider} API keys configured.")
|
|
20
19
|
return res
|
|
21
20
|
|
|
22
21
|
|
|
23
|
-
def prep_agent_launch(agents_dir: Path, prompts_material: list[str], prompt_prefix: str, keep_material_in_separate_file: bool,
|
|
22
|
+
def prep_agent_launch(repo_root: Path, agents_dir: Path, prompts_material: list[str], prompt_prefix: str, keep_material_in_separate_file: bool,
|
|
23
|
+
machine: MATCHINE, model: MODEL, provider: PROVIDER, agent: AGENTS, *, job_name: str) -> None:
|
|
24
24
|
agents_dir.mkdir(parents=True, exist_ok=True)
|
|
25
25
|
prompt_folder = agents_dir / "prompts"
|
|
26
26
|
prompt_folder.mkdir(parents=True, exist_ok=True)
|
|
@@ -32,21 +32,22 @@ def prep_agent_launch(agents_dir: Path, prompts_material: list[str], prompt_pref
|
|
|
32
32
|
if keep_material_in_separate_file:
|
|
33
33
|
prompt_material_path = prompt_root / f"agent_{idx}_material.txt"
|
|
34
34
|
prompt_material_path.write_text(a_prompt_material, encoding="utf-8")
|
|
35
|
-
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")
|
|
35
|
+
prompt_path.write_text(prompt_prefix + f"""\nPlease only look @ {prompt_material_path.relative_to(repo_root)}. You don't need to do any other work beside the content of this material file.""", encoding="utf-8")
|
|
36
36
|
else:
|
|
37
37
|
prompt_material_path = prompt_path
|
|
38
38
|
prompt_path.write_text(prompt_prefix + """\nPlease only look @ the following:\n""" + a_prompt_material, encoding="utf-8")
|
|
39
39
|
|
|
40
40
|
agent_cmd_launch_path = prompt_root / AGENT_NAME_FORMATTER.format(idx=idx) # e.g., agent_0_cmd.sh
|
|
41
41
|
random_sleep_time = random.uniform(0, 5)
|
|
42
|
-
cmd_prefix = f"""
|
|
43
|
-
#!/usr/bin/env bash
|
|
44
|
-
|
|
45
|
-
sleep 5
|
|
46
|
-
timeout 3 copilot --banner
|
|
42
|
+
cmd_prefix = f"""#!/usr/bin/env bash
|
|
47
43
|
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
echo "Using machine: {machine}, model: {model}, provider: {provider}, and agent: {agent}"
|
|
45
|
+
echo "{job_name}-{idx} CMD {agent_cmd_launch_path}"
|
|
46
|
+
echo "{job_name}-{idx} PROMPT {prompt_path}"
|
|
47
|
+
echo "{job_name}-{idx} CONTEXT {prompt_material_path}"
|
|
48
|
+
echo "Starting agent {agent} in 5 seconds... Press Ctrl+C to cancel."
|
|
49
|
+
# sleep 5
|
|
50
|
+
# timeout 3 copilot --banner
|
|
50
51
|
|
|
51
52
|
export FIRE_AGENTS_AGENT_NAME="{agent}"
|
|
52
53
|
export FIRE_AGENTS_JOB_NAME="{job_name}"
|
|
@@ -56,56 +57,37 @@ export FIRE_AGENTS_AGENT_LAUNCHER="{agent_cmd_launch_path}"
|
|
|
56
57
|
|
|
57
58
|
echo "Sleeping for {random_sleep_time:.2f} seconds to stagger agent startups..."
|
|
58
59
|
sleep {random_sleep_time:.2f}
|
|
59
|
-
echo "Launching agent {agent} with prompt from {prompt_path}"
|
|
60
|
-
echo "Launching agent {agent} with command from {agent_cmd_launch_path}"
|
|
61
60
|
echo "--------START OF AGENT OUTPUT--------"
|
|
62
61
|
sleep 0.1
|
|
63
62
|
|
|
64
63
|
"""
|
|
65
64
|
match agent:
|
|
66
65
|
case "gemini":
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
# else:
|
|
73
|
-
model_arg = f"--model {shlex.quote(model)}"
|
|
74
|
-
# Need a real shell for the pipeline; otherwise '| gemini ...' is passed as args to 'cat'
|
|
75
|
-
safe_path = shlex.quote(str(prompt_path))
|
|
76
|
-
api_keys = get_gemini_api_keys()
|
|
77
|
-
api_key = api_keys[idx % len(api_keys)] if api_keys else ""
|
|
78
|
-
# Export the environment variable so it's available to subshells
|
|
79
|
-
cmd = f"""
|
|
80
|
-
|
|
81
|
-
export GEMINI_API_KEY={shlex.quote(api_key)}
|
|
82
|
-
echo "Using Gemini API key $GEMINI_API_KEY"
|
|
83
|
-
|
|
84
|
-
gemini {model_arg} --yolo --prompt {safe_path}
|
|
85
|
-
"""
|
|
66
|
+
assert provider == "google", "Gemini agent only works with google provider."
|
|
67
|
+
api_keys = get_api_keys(provider="google")
|
|
68
|
+
api_key = api_keys[idx % len(api_keys)] if len(api_keys) > 0 else None
|
|
69
|
+
from machineconfig.scripts.python.helpers_fire.fire_gemini import fire_gemini
|
|
70
|
+
cmd = fire_gemini(api_key=api_key, prompt_path=prompt_path, machine=machine)
|
|
86
71
|
case "cursor-agent":
|
|
87
|
-
|
|
88
|
-
cmd =
|
|
89
|
-
|
|
90
|
-
cursor-agent --print --output-format text {prompt_path}
|
|
91
|
-
|
|
92
|
-
"""
|
|
72
|
+
from machineconfig.scripts.python.helpers_fire.fire_cursor_agents import fire_cursor
|
|
73
|
+
cmd = fire_cursor(prompt_path=prompt_path, machine=machine, api_key=None)
|
|
74
|
+
raise NotImplementedError("Cursor agent is not implemented yet, api key missing")
|
|
93
75
|
case "crush":
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
76
|
+
from machineconfig.scripts.python.helpers_fire.fire_crush import fire_crush
|
|
77
|
+
api_keys = get_api_keys(provider=provider)
|
|
78
|
+
api_key = api_keys[idx % len(api_keys)] if len(api_keys) > 0 else None
|
|
79
|
+
cmd = fire_crush(api_key=api_key, prompt_path=prompt_path, machine=machine, repo_root=repo_root, model=model, provider=provider)
|
|
97
80
|
case "q":
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
"""
|
|
81
|
+
from machineconfig.scripts.python.helpers_fire.fire_q import fire_q
|
|
82
|
+
cmd = fire_q(api_key="", prompt_path=prompt_path, machine=machine)
|
|
101
83
|
case _:
|
|
102
84
|
raise ValueError(f"Unsupported agent type: {agent}")
|
|
85
|
+
|
|
103
86
|
cmd_postfix = """
|
|
104
87
|
sleep 0.1
|
|
105
88
|
echo "---------END OF AGENT OUTPUT---------"
|
|
106
89
|
"""
|
|
107
90
|
agent_cmd_launch_path.write_text(cmd_prefix + cmd + cmd_postfix, encoding="utf-8")
|
|
108
|
-
|
|
109
91
|
return None
|
|
110
92
|
|
|
111
93
|
|
|
@@ -1,12 +1,30 @@
|
|
|
1
1
|
|
|
2
|
-
from typing import Literal, TypeAlias
|
|
2
|
+
from typing import Literal, TypeAlias, TypedDict
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
AGENTS: TypeAlias = Literal[
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
AGENTS: TypeAlias = Literal["cursor-agent", "gemini", "crush", "q", "opencode"]
|
|
6
|
+
MATCHINE: TypeAlias = Literal["local", "docker"]
|
|
7
|
+
PROVIDER: TypeAlias = Literal["azure", "google", "aws", "openai", "anthropic", "openrouter", "xai"]
|
|
8
|
+
MODEL: TypeAlias = Literal["zai/glm-4.6", "anthropic/sonnet-4.5", "google/gemini-2.5-pro", "openai/gpt-5-codex",
|
|
9
|
+
"openrouter/supernova", "x-ai/grok-4-fast:free",
|
|
10
|
+
]
|
|
11
|
+
PROVIDER2MODEL: dict[PROVIDER, list[MODEL]] = {
|
|
12
|
+
"azure": ["zai/glm-4.6"],
|
|
13
|
+
"google": ["google/gemini-2.5-pro"],
|
|
14
|
+
"aws": [],
|
|
15
|
+
"openai": ["openai/gpt-5-codex"],
|
|
16
|
+
"anthropic": ["anthropic/sonnet-4.5"],
|
|
17
|
+
"openrouter": ["openrouter/supernova"],
|
|
18
|
+
"xai": ["x-ai/grok-4-fast:free"]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
class AI_SPEC(TypedDict):
|
|
22
|
+
provider: PROVIDER
|
|
23
|
+
model: MODEL
|
|
24
|
+
agent: AGENTS
|
|
25
|
+
machine: MATCHINE
|
|
10
26
|
|
|
27
|
+
|
|
28
|
+
AGENT_NAME_FORMATTER = "agent_{idx}_cmd.sh" # e.g., agent_0_cmd.sh
|
|
11
29
|
SEARCH_STRATEGIES: TypeAlias = Literal["file_path", "keyword_search", "filename_pattern"]
|
|
12
30
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
# import shlex
|
|
4
|
+
from typing import Optional
|
|
5
|
+
from machineconfig.scripts.python.helpers_fire.fire_agents_helper_types import MATCHINE, PROVIDER, MODEL
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def fire_crush(api_key: Optional[str], model: MODEL, provider: PROVIDER, machine: MATCHINE, prompt_path: Path, repo_root: Path) -> str:
|
|
9
|
+
match machine:
|
|
10
|
+
case "local":
|
|
11
|
+
cmd = f"""
|
|
12
|
+
crush run {prompt_path}
|
|
13
|
+
"""
|
|
14
|
+
case "docker":
|
|
15
|
+
assert api_key is not None, "API key is required for Crush agent in docker mode."
|
|
16
|
+
json_path = Path(__file__).parent / "fire_crush.json"
|
|
17
|
+
json_template = json_path.read_text(encoding="utf-8")
|
|
18
|
+
json_filled = json_template.replace("{api_key}", api_key).replace("{model}", model).replace("{provider}", provider)
|
|
19
|
+
import tempfile
|
|
20
|
+
temp_config_file_local = tempfile.mkstemp(suffix=".json")[1]
|
|
21
|
+
Path(temp_config_file_local).write_text(json_filled, encoding="utf-8")
|
|
22
|
+
cmd = f"""
|
|
23
|
+
|
|
24
|
+
# -e "PATH_PROMPT=$PATH_PROMPT"
|
|
25
|
+
# opencode --model "{provider}/{model}" run {prompt_path}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
echo "Running prompt @ {prompt_path.relative_to(repo_root)} using Docker with Crush..."
|
|
29
|
+
docker run -it --rm \
|
|
30
|
+
-v "{repo_root}:/workspace/{repo_root.name}" \
|
|
31
|
+
-v "{temp_config_file_local}:/root/.local/share/crush/crush.json" \
|
|
32
|
+
-w "/workspace/{repo_root.name}" \
|
|
33
|
+
statistician/machineconfig:latest \
|
|
34
|
+
bash -i -c "source ~/.bashrc && cd /workspace/{repo_root.name} && cat /root/.local/share/crush/crush.json && crush run 'Please act on contents of this prompt ./{prompt_path.relative_to(repo_root)}'"
|
|
35
|
+
|
|
36
|
+
"""
|
|
37
|
+
return cmd
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
# import shlex
|
|
5
|
+
from machineconfig.scripts.python.helpers_fire.fire_agents_helper_types import MATCHINE
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
def fire_cursor(api_key: Optional[str], prompt_path: Path, machine: MATCHINE) -> str:
|
|
9
|
+
match machine:
|
|
10
|
+
case "local":
|
|
11
|
+
# Export the environment variable so it's available to subshells
|
|
12
|
+
cmd = f"""
|
|
13
|
+
|
|
14
|
+
cursor-agent --print --output-format text {prompt_path}
|
|
15
|
+
|
|
16
|
+
"""
|
|
17
|
+
case "docker":
|
|
18
|
+
cmd = f"""
|
|
19
|
+
|
|
20
|
+
cursor-agent --print --output-format text {prompt_path}
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
return cmd
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import shlex
|
|
4
|
+
from machineconfig.scripts.python.helpers_fire.fire_agents_helper_types import MATCHINE
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def fire_gemini(api_key: Optional[str], prompt_path: Path, machine: MATCHINE) -> str:
|
|
9
|
+
model = "gemini-2.5-pro"
|
|
10
|
+
# model = "gemini-2.5-flash-lite"
|
|
11
|
+
# model = None # auto-select
|
|
12
|
+
# if model is None:
|
|
13
|
+
# model_arg = ""
|
|
14
|
+
# else:
|
|
15
|
+
model_arg = f"--model {shlex.quote(model)}"
|
|
16
|
+
# Need a real shell for the pipeline; otherwise '| gemini ...' is passed as args to 'cat'
|
|
17
|
+
safe_path = shlex.quote(str(prompt_path))
|
|
18
|
+
|
|
19
|
+
match machine:
|
|
20
|
+
case "local":
|
|
21
|
+
# Export the environment variable so it's available to subshells
|
|
22
|
+
if api_key is not None:
|
|
23
|
+
define_api_key = f"""export GEMINI_API_KEY="{shlex.quote(api_key)}" """
|
|
24
|
+
else:
|
|
25
|
+
define_api_key = "echo 'Warning: No GEMINI_API_KEY provided, hoping it is set in the environment.'"
|
|
26
|
+
cmd = f"""
|
|
27
|
+
{define_api_key}
|
|
28
|
+
echo "Using Gemini API key $GEMINI_API_KEY"
|
|
29
|
+
gemini {model_arg} --yolo --prompt {safe_path}
|
|
30
|
+
"""
|
|
31
|
+
case "docker":
|
|
32
|
+
cmd = """
|
|
33
|
+
docker run -it --rm \
|
|
34
|
+
-e GEMINI_API_KEY="$GEMINI_API_KEY" \
|
|
35
|
+
-v "/home/alex/code/machineconfig:/workspace/machineconfig" \
|
|
36
|
+
-w "/workspace/machineconfig" \
|
|
37
|
+
gemini-cli:latest \
|
|
38
|
+
gemini --prompt "$PATH_PROMPT"
|
|
39
|
+
"""
|
|
40
|
+
return cmd
|
|
41
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import shlex
|
|
4
|
+
from machineconfig.scripts.python.helpers_fire.fire_agents_helper_types import MATCHINE
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def fire_q(api_key: str, prompt_path: Path, machine: MATCHINE) -> str:
|
|
8
|
+
safe_path = shlex.quote(str(prompt_path))
|
|
9
|
+
|
|
10
|
+
match machine:
|
|
11
|
+
case "local":
|
|
12
|
+
cmd = f"""
|
|
13
|
+
q chat --no-interactive --trust-all-tools {safe_path}
|
|
14
|
+
"""
|
|
15
|
+
case "docker":
|
|
16
|
+
cmd = f"""
|
|
17
|
+
q chat --no-interactive --trust-all-tools {safe_path}
|
|
18
|
+
"""
|
|
19
|
+
return cmd
|
|
File without changes
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
|
|
2
|
+
#!/bin/bash
|
|
3
|
+
# set -e # Exit immediately if a command exits with a non-zero status.
|
|
4
|
+
|
|
5
|
+
JOB_NAME="outpatient_mapping"
|
|
6
|
+
REPO_ROOT="$HOME/code/machineconfig"
|
|
7
|
+
CONTEXT_PATH="$REPO_ROOT/src/machineconfig/scripts/python/fire_jobs.py"
|
|
8
|
+
PROMPT_PATH="$REPO_ROOT/src/machineconfig/scripts/python/helpers_fire/prompt.txt"
|
|
9
|
+
|
|
10
|
+
AGENTS_DIR="$REPO_ROOT/.ai/agents/$JOB_NAME"
|
|
11
|
+
LAYOUT_PATH_UNBALANCED="$REPO_ROOT/.ai/agents/$JOB_NAME/layout_unbalanced.json"
|
|
12
|
+
|
|
13
|
+
agents create \
|
|
14
|
+
--context-path "$CONTEXT_PATH" \
|
|
15
|
+
--tasks-per-prompt 1 \
|
|
16
|
+
--machine docker \
|
|
17
|
+
--agent crush \
|
|
18
|
+
--model "zai/glm-4.6" \
|
|
19
|
+
--provider openrouter \
|
|
20
|
+
--separator 'def ' \
|
|
21
|
+
--prompt-path "$PROMPT_PATH" \
|
|
22
|
+
--output-path "$LAYOUT_PATH_UNBALANCED" \
|
|
23
|
+
--agents-dir "$AGENTS_DIR"
|
|
24
|
+
|
|
25
|
+
# LAYOUT_BALANCED_PATH="$REPO_ROOT/.ai/agents/$JOB_NAME/layout_balanced.json"
|
|
26
|
+
# sessions balance-load $LAYOUT_PATH --max-thresh 6 --breaking-method moreLayouts --thresh-type number --output-path $LAYOUT_BALANCED_PATH
|
|
27
|
+
# sessions run $LAYOUT_BALANCED_PATH --kill-upon-completion
|
|
28
|
+
|
|
29
|
+
sessions run $LAYOUT_PATH_UNBALANCED
|
|
30
|
+
|
|
31
|
+
# agents collect $AGENTS_DIR "$REPO_ROOT/.ai/agents/$JOB_NAME/collected.txt"
|
|
@@ -95,17 +95,22 @@ def display_dotfiles_instructions() -> None:
|
|
|
95
95
|
|
|
96
96
|
def get_installation_choices() -> list[str]:
|
|
97
97
|
"""Get user choices for installation options."""
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
Choice(value="
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
Choice(value="
|
|
106
|
-
Choice(value="
|
|
107
|
-
Choice(value="
|
|
108
|
-
Choice(value="
|
|
98
|
+
if platform.system() == "Windows":
|
|
99
|
+
choices = []
|
|
100
|
+
else:
|
|
101
|
+
choices = [
|
|
102
|
+
Choice(value="upgrade_system", title="🔄 Upgrade System Package Manager", checked=False),
|
|
103
|
+
]
|
|
104
|
+
choices += [
|
|
105
|
+
Choice(value="install_repos", title="🐍 Install machineconfig.", checked=False),
|
|
106
|
+
Choice(value="ESSENTIAL_SYSTEM", title="📥 Install Essential System Packages.", checked=False),
|
|
107
|
+
Choice(value="ESSENTIAL", title="⚡ Install CLI apps essentials", checked=False),
|
|
108
|
+
Choice(value="DEV_SYSTEM", title="🛠️ Install CLI apps development.", checked=False),
|
|
109
|
+
Choice(value="TerminalEyeCandy", title="🎨 Install CLI apps terminal eye candy.", checked=False),
|
|
110
|
+
Choice(value="install_ssh_server", title="🔒 Install SSH Server", checked=False),
|
|
111
|
+
Choice(value="install_shell_profile", title="🐚 Configure Shell Profile.", checked=False),
|
|
112
|
+
Choice(value="retrieve_repositories", title="📚 Retrieve Repositories", checked=False),
|
|
113
|
+
Choice(value="retrieve_data", title="💾 Retrieve Data.", checked=False),
|
|
109
114
|
]
|
|
110
115
|
# Add Windows-specific options
|
|
111
116
|
if platform.system() == "Windows":
|
|
@@ -117,12 +122,10 @@ def get_installation_choices() -> list[str]:
|
|
|
117
122
|
def execute_installations(selected_options: list[str]) -> None:
|
|
118
123
|
if platform.system() == "Windows":
|
|
119
124
|
from machineconfig import setup_windows as module
|
|
120
|
-
|
|
121
|
-
run_shell_script(script_path.read_text(encoding="utf-8"))
|
|
125
|
+
run_shell_script(module.UV.read_text(encoding="utf-8"))
|
|
122
126
|
else:
|
|
123
127
|
from machineconfig import setup_linux as module
|
|
124
|
-
|
|
125
|
-
run_shell_script(script_path.read_text(encoding="utf-8"))
|
|
128
|
+
run_shell_script(module.UV.read_text(encoding="utf-8"))
|
|
126
129
|
|
|
127
130
|
for maybe_a_group in selected_options:
|
|
128
131
|
if maybe_a_group in ("ESSENTIAL", "DEV", "ESSENTIAL_SYSTEM", "DEV_SYSTEM", "TerminalEyeCandy"):
|
|
@@ -147,10 +150,10 @@ def execute_installations(selected_options: list[str]) -> None:
|
|
|
147
150
|
if "install_repos" in selected_options:
|
|
148
151
|
console.print(Panel("🐍 [bold green]PYTHON ENVIRONMENT[/bold green]\n[italic]Virtual environment setup[/italic]", border_style="green"))
|
|
149
152
|
if platform.system() == "Windows":
|
|
150
|
-
from machineconfig.setup_windows import
|
|
153
|
+
from machineconfig.setup_windows import MACHINECONFIG
|
|
151
154
|
else:
|
|
152
|
-
from machineconfig.setup_linux import
|
|
153
|
-
run_shell_script(
|
|
155
|
+
from machineconfig.setup_linux import MACHINECONFIG
|
|
156
|
+
run_shell_script(MACHINECONFIG.read_text(encoding="utf-8"))
|
|
154
157
|
|
|
155
158
|
if "install_ssh_server" in selected_options:
|
|
156
159
|
console.print(Panel("🔒 [bold red]SSH SERVER[/bold red]\n[italic]Remote access setup[/italic]", border_style="red"))
|
|
@@ -207,7 +210,6 @@ def main() -> None:
|
|
|
207
210
|
console.print("❌ Installation cancelled.", style="bold red")
|
|
208
211
|
sys.exit(0)
|
|
209
212
|
execute_installations(selected_options)
|
|
210
|
-
display_dotfiles_instructions()
|
|
211
213
|
display_completion_message()
|
|
212
214
|
|
|
213
215
|
|
|
@@ -8,13 +8,13 @@ in the event that username@github.com is not mentioned in the remote url.
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from typing import Annotated, Optional
|
|
10
10
|
import typer
|
|
11
|
+
from machineconfig.scripts.python.secure_repo import main as secure_repo_main
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
app = typer.Typer(help="� Manage development repositories", no_args_is_help=True)
|
|
14
15
|
sync_app = typer.Typer(help="� Manage repository specifications and syncing", no_args_is_help=True)
|
|
15
16
|
app.add_typer(sync_app, name="sync", help="� Sync repositories using saved specs")
|
|
16
17
|
|
|
17
|
-
|
|
18
18
|
DirectoryArgument = Annotated[Optional[str], typer.Argument(help="📁 Folder containing repos or the specs JSON file to use.")]
|
|
19
19
|
RecursiveOption = Annotated[bool, typer.Option("--recursive", "-r", help="🔍 Recurse into nested repositories.")]
|
|
20
20
|
NoSyncOption = Annotated[bool, typer.Option("--no-sync", help="🚫 Disable automatic uv sync after pulls.")]
|
|
@@ -101,6 +101,9 @@ def analyze(directory: DirectoryArgument = None) -> None:
|
|
|
101
101
|
analyze_repo_development(repo_path=repo_path)
|
|
102
102
|
|
|
103
103
|
|
|
104
|
+
app.command(name="secure", no_args_is_help=True, help="🔐 Securely sync git repository to/from cloud with encryption")(secure_repo_main)
|
|
105
|
+
|
|
106
|
+
|
|
104
107
|
@app.command(no_args_is_help=True)
|
|
105
108
|
def viz(
|
|
106
109
|
repo: str = typer.Option(Path.cwd().__str__(), "--repo", "-r", help="Path to git repository to visualize"),
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
import typer
|
|
3
|
+
from typing import Optional, Literal
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def main(
|
|
8
|
+
cloud: Optional[str] = typer.Option(None, "--cloud", "-c", help="Cloud storage profile name. If not provided, uses default from config."),
|
|
9
|
+
repo: Optional[str] = typer.Option(None, "--repo", "-r", help="Path to the local repository. Defaults to current working directory."),
|
|
10
|
+
message: Optional[str] = typer.Option(None, "--message", "-m", help="Commit message for local changes."),
|
|
11
|
+
on_conflict: Literal["ask", "pushLocalMerge", "overwriteLocal", "InspectRepos", "RemoveLocalRclone"] = typer.Option("ask", "--on-conflict", "-oc", help="Action to take on merge conflict. Default is 'ask'."),
|
|
12
|
+
pwd: Optional[str] = typer.Option(None, "--password", help="Password for encryption/decryption of the remote repository."),
|
|
13
|
+
):
|
|
14
|
+
from machineconfig.scripts.python.cloud_repo_sync import main as program_content
|
|
15
|
+
program_content(cloud=cloud, repo=repo, message=message, on_conflict=on_conflict, pwd=pwd)
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
#=======================================================================
|
|
3
2
|
# 🌳 BROOT SHELL INTEGRATION 🌳
|
|
4
|
-
#=======================================================================
|
|
5
3
|
# This script was automatically generated by the broot program
|
|
6
4
|
# More information can be found in https://github.com/Canop/broot
|
|
7
5
|
#
|
|
@@ -40,7 +38,6 @@ function br {
|
|
|
40
38
|
fi
|
|
41
39
|
}
|
|
42
40
|
|
|
43
|
-
#=======================================================================
|
|
44
41
|
# ℹ️ USAGE:
|
|
45
42
|
# br [options]
|
|
46
43
|
#
|
|
@@ -48,4 +45,3 @@ function br {
|
|
|
48
45
|
# br # Open broot in the current directory
|
|
49
46
|
# br /path/to/dir # Open broot in the specified directory
|
|
50
47
|
# br -h # Show broot help
|
|
51
|
-
#=======================================================================
|