From d8c3b30f7d4dc0016306d9139c02c5eeb70f54b0 Mon Sep 17 00:00:00 2001 From: Fine_Computer_4451 <119702188+FineComputer14451@users.noreply.github.com> Date: Wed, 2 Sep 2026 14:37:22 -0700 Subject: [PATCH 1/2] feat(profile): sticky permission_mode and yolo across --force --- config/grok-build.nethunter.toml | 1 + docs/GROK-BUILD-1.0.md | 1 + scripts/install_grok_profile.sh | 41 ++++++++++++++++++++++++-------- 3 files changed, 33 insertions(+), 10 deletions(-) mode change 100755 => 100644 scripts/install_grok_profile.sh diff --git a/config/grok-build.nethunter.toml b/config/grok-build.nethunter.toml index d9f5102..84a3971 100644 --- a/config/grok-build.nethunter.toml +++ b/config/grok-build.nethunter.toml @@ -31,6 +31,7 @@ screen_mode = "fullscreen" max_thoughts_width = 100 # Secondary model for forks / side tasks (must be a real catalog id) fork_secondary_model = "grok-4.6" +# Sticky across --force (reset with --force-ui). Template never sets yolo=true. yolo = false compact_mode = true permission_mode = "always-approve" diff --git a/docs/GROK-BUILD-1.0.md b/docs/GROK-BUILD-1.0.md index b01427c..aec0dfa 100644 --- a/docs/GROK-BUILD-1.0.md +++ b/docs/GROK-BUILD-1.0.md @@ -74,3 +74,4 @@ cat ~/.grok/config.toml | head -40 - Session auth (SuperGrok / X Premium+) and `XAI_API_KEY` both work; prefer secrets in `~/.grok/secrets.env`. - V9 picker aliases are **local** `[model.*]` names that still call **grok-4.6**. Former 4.5 picker IDs remain as compat aliases. - `grok-build` as a **model id** is not the lab default — use `grok-4.6` (agent type `plan` is separate from model id). +- `[ui].permission_mode` and `[ui].yolo` survive `install_grok_profile.sh --force`. Restore product defaults with `--force-ui`. Template ships `always-approve` + `yolo=false`. diff --git a/scripts/install_grok_profile.sh b/scripts/install_grok_profile.sh old mode 100755 new mode 100644 index c82388d..bda5b1d --- a/scripts/install_grok_profile.sh +++ b/scripts/install_grok_profile.sh @@ -3,12 +3,14 @@ # # - Does NOT delete custom [model.*] blocks or unrelated user keys # - Overwrites known profile sections/keys from config/grok-build.nethunter.toml +# - Sticky [ui] keys (permission_mode, yolo) survive --force; reset with --force-ui # - Safe to re-run (idempotent) # # Usage: # bash scripts/install_grok_profile.sh # GROK_CONFIG=/path/to/config.toml bash scripts/install_grok_profile.sh # bash scripts/install_grok_profile.sh --force +# bash scripts/install_grok_profile.sh --force-ui set -euo pipefail info() { echo "[install_grok_profile] $*"; } @@ -20,12 +22,17 @@ SRC="${GROKHUNTER_PROFILE_SRC:-$ROOT/config/grok-build.nethunter.toml}" CFG="${GROK_CONFIG:-${HOME:?HOME not set}/.grok/config.toml}" MARKER="# --- GrokHunter NetHunter profile (Grok Build 1.0.5+) ---" FORCE=0 +FORCE_UI=0 for arg in "$@"; do case "$arg" in --force|-f) FORCE=1 ;; + --force-ui) + FORCE=1 + FORCE_UI=1 + ;; --help|-h) - sed -n '2,14p' "$0" || true + sed -n '2,16p' "$0" || true exit 0 ;; *) die "Unknown option: $arg" ;; @@ -43,12 +50,13 @@ if [[ ! -f "$CFG" ]]; then fi # Parse simple TOML subset: [section] and key = value (no nested tables as keys) -python3 - "$SRC" "$CFG" "$MARKER" "$FORCE" <<'PY' +python3 - "$SRC" "$CFG" "$MARKER" "$FORCE" "$FORCE_UI" <<'PY' from pathlib import Path import re import sys src_path, cfg_path, marker, force = Path(sys.argv[1]), Path(sys.argv[2]), sys.argv[3], sys.argv[4] == "1" +force_ui = sys.argv[5] == "1" if len(sys.argv) > 5 else False # Sections we own and fully replace key-wise (nested table ui.display_refresh included) OWNED = { @@ -62,6 +70,12 @@ OWNED = { "subagents", } +# Phone-tuned approvals must survive --force (stale 4.5 / alpha repair). +# --force-ui restores product defaults from the template. +STICKY_UI = {"permission_mode", "yolo"} +YOLO_RE = re.compile(r"^(true|false)$", re.I) +PERM_RE = re.compile(r'^"[A-Za-z][A-Za-z0-9_-]*"$') + def parse_toml_simple(text: str): """Return {section: {key: raw_value_line}} preserving raw RHS text.""" sections = {"": {}} @@ -86,6 +100,19 @@ def parse_toml_simple(text: str): src = parse_toml_simple(src_path.read_text(encoding="utf-8")) cfg_text = cfg_path.read_text(encoding="utf-8") +cfg_parsed = parse_toml_simple(cfg_text) + +def sticky_ui_value(key: str, template_val: str) -> str: + if force_ui: + return template_val + prev = cfg_parsed.get("ui", {}).get(key) + if not prev: + return template_val + if key == "yolo" and YOLO_RE.match(prev): + return prev.lower() + if key == "permission_mode" and PERM_RE.match(prev): + return prev + return template_val # Already applied? (skip unless --force) if marker in cfg_text and not force: @@ -109,9 +136,6 @@ if marker in cfg_text and not force: print("profile already present (use --force to refresh)") sys.exit(0) -# Strip previous marker block (from marker to next blank-line-before-[ or EOF we don't own) -# Safer: strip owned sections entirely, then re-append. - def strip_owned_sections(text: str) -> str: lines = text.splitlines(keepends=True) out = [] @@ -120,8 +144,6 @@ def strip_owned_sections(text: str) -> str: line = lines[i] m = re.match(r"^\[([^\]]+)\]\s*$", line.strip()) if m and m.group(1).strip() in OWNED: - # skip until next section or EOF — but keep sibling feature - # markers (V9 pickers comment lives after [models]) i += 1 while i < len(lines): stripped = lines[i].strip() @@ -131,7 +153,6 @@ def strip_owned_sections(text: str) -> str: break i += 1 continue - # Drop old marker comment lines if marker in line or line.strip().startswith("# GrokHunter Rootless") \ or line.strip().startswith("# Applied by scripts/install_grok_profile"): i += 1 @@ -145,9 +166,7 @@ def strip_owned_sections(text: str) -> str: base = strip_owned_sections(cfg_text) -# Build owned sections from SRC only parts = [base.rstrip(), "", marker] -# Preserve template comments header lightly parts.append("# Managed keys for Grok Build 1.0.5+ (safe to re-run install_grok_profile.sh)") order = ["hints", "features", "telemetry", "cli", "ui", "ui.display_refresh", "models", "subagents"] @@ -157,6 +176,8 @@ for sec in order: parts.append("") parts.append(f"[{sec}]") for key, val in src[sec].items(): + if sec == "ui" and key in STICKY_UI: + val = sticky_ui_value(key, val) parts.append(f"{key} = {val}") new_text = "\n".join(parts).rstrip() + "\n" From d1ce5a9ba71946aed1facd0608b3e546fe7850d4 Mon Sep 17 00:00:00 2001 From: Fine_Computer_4451 <119702188+FineComputer14451@users.noreply.github.com> Date: Wed, 2 Sep 2026 14:38:07 -0700 Subject: [PATCH 2/2] docs: note sticky UI profile keys in CHANGELOG --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbd1dbc..dca195d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +### Profile +- `install_grok_profile.sh`: `[ui].permission_mode` and `[ui].yolo` are sticky across `--force` (stale 4.5 / alpha / `grok-build` slug repair). `--force-ui` restores product defaults (`always-approve`, `yolo=false`). Template still ships lab-fast approvals; yolo stays false. + ### Installer TUI - First-run numbered wizard on Termux: bare `install.sh` (TTY, no size/feature flags) shows a checklist, prints argv, then `exec`s the existing engine. Default is **coding-only** (`--nano --no-de --with-grok --with-completions`). Desktop toggle promotes to `--full --de xfce --browser chromium --with-x11`. `--yes` is the default with no wizard. Flags still skip it. Overlay-only is unchanged. Overlay cache **2026.8.27**. Never prompts for a key.