diff --git a/.claude/scripts/migrate-identity.sh b/.claude/scripts/migrate-identity.sh index 088d417..51b49e2 100755 --- a/.claude/scripts/migrate-identity.sh +++ b/.claude/scripts/migrate-identity.sh @@ -75,6 +75,27 @@ else echo " Prose model: qwen3:14b (default)" fi +# Detect Grok CLI (xAI) — optional capability extension (image/video, live web+X +# data, independent second model). Per-machine; sets identity grok.installed so +# the /grok skill knows whether it can run locally. Does NOT set is_fleet_host +# (that's a manual fleet-coordination choice, preserved if already present). +GROK_BIN="" +if command -v grok >/dev/null 2>&1; then + GROK_BIN="$(command -v grok)" +else + for c in "$HOME/.grok/bin/grok.exe" "$HOME/.grok/bin/grok" "${LOCALAPPDATA:-}/Programs/grok/grok.exe"; do + if [ -x "$c" ]; then GROK_BIN="$c"; break; fi + done +fi +if [ -n "$GROK_BIN" ]; then + GROK_BIN="$(cygpath -m "$GROK_BIN" 2>/dev/null || echo "$GROK_BIN")" + GROK_INSTALLED="true" + echo " Grok: installed ($GROK_BIN)" +else + GROK_INSTALLED="false" + echo " Grok: not installed" +fi + # Build updated identity.json echo "" echo "[INFO] Updating identity.json..." @@ -104,6 +125,17 @@ if 'platform' not in data: if 'architecture' not in data: data['architecture'] = '$ARCH' +# Grok capability flag (per-machine). Preserve manual keys like is_fleet_host. +g = data.get('grok') or {} +if '$GROK_INSTALLED' == 'true': + g['installed'] = True + g['binary'] = r'$GROK_BIN' + g.setdefault('auth', 'oidc') + g.setdefault('capabilities', ['text', 'verify', 'image', 'video', 'xsearch']) +else: + g['installed'] = False +data['grok'] = g + # Coord API endpoint — populate only if absent so existing machines keep their override. if 'coord_api' not in data: data['coord_api'] = '$COORD_API_DEFAULT' @@ -125,6 +157,7 @@ echo " ollama.fallback: $OLLAMA_FALLBACK" echo " ollama.prose_model: $PROSE_MODEL" echo " platform: $PLATFORM" echo " architecture: $ARCH" +echo " grok.installed: $GROK_INSTALLED" echo " coord_api: (default $COORD_API_DEFAULT if not already set)" echo "" echo "Review: cat $IDENTITY_PATH" diff --git a/.claude/skills/grok/SKILL.md b/.claude/skills/grok/SKILL.md new file mode 100644 index 0000000..6d421bf --- /dev/null +++ b/.claude/skills/grok/SKILL.md @@ -0,0 +1,88 @@ +--- +name: grok +description: > + Route a task to the Grok CLI (xAI Grok 4.3) for capabilities Claude lacks or + for an independent second model. Use for: IMAGE generation/editing, VIDEO + generation (image->video), live WEB + X/TWITTER search (current/real-time + data past Claude's cutoff), and adversarial second-opinion VERIFICATION or + drafts. Invoke on: "ask grok", "grok image", "generate/make an image", + "make a video / animate this", "grok verify / second opinion from grok", + "search X / twitter", "what's the latest ". Grok is a + capability EXTENSION (image/video/live-data), not a replacement for Claude's + own coding/editing. +--- + +# Grok capability router + +Claude shells out to the locally-installed **Grok CLI** (`grok.exe`, xAI Grok 4.3) +for things Claude can't do natively, or for a genuinely independent second model. +Verified working on this machine (2026-06-04): image gen, image->video, live +web/X search, text reasoning. + +**Auth:** Grok uses its own OIDC login (`~/.grok/auth.json`, grok.com, ~6h refresh) +— **no API key**. If calls fail with auth errors, the user runs `grok login`. + +## The wrapper + +``` +bash "$CLAUDETOOLS_ROOT/.claude/skills/grok/scripts/ask-grok.sh" ... +``` + +| Mode | Usage | What it does | +|------|-------|--------------| +| `text` | `ask-grok.sh text ""` | One-shot text answer (independent model). Prints to stdout. | +| `verify` | `ask-grok.sh verify ""` | Adversarial second opinion — Grok tries to REFUTE, returns CONFIRMED/REFUTED + reason. | +| `image` | `ask-grok.sh image "" [out.png]` | `image_gen` (Imagine) → copies the artifact to `out` (default `grok-image.png`). | +| `video` | `ask-grok.sh video "" [out.mp4]` | `image_to_video` on an input image → copies to `out`. ~60-90s. | +| `xsearch` | `ask-grok.sh xsearch ""` | Live `web_search` + X/Twitter tools; returns text with citations. | +| `raw` | `ask-grok.sh raw ` | Escape hatch — passes args straight to `grok`. | + +The script captures JSON (`--output-format json`), parses the result, and for +media **retrieves the artifact by sessionId** from +`~/.grok/sessions///{images,videos}/` — so artifacts are +recovered even when a headless run reports `stopReason: Cancelled` before echoing +the path (a known finalization quirk of the `-p` mode). + +## Machine availability (fleet) + +Grok is **per-machine** — the skill syncs fleet-wide but the binary does not. Availability is gated by `identity.json` (per-machine, gitignored): + +```json +"grok": { "installed": true, "binary": "C:/Users/guru/.grok/bin/grok.exe", + "auth": "oidc", "is_fleet_host": true, + "capabilities": ["text","verify","image","video","xsearch"] } +``` + +- If `grok.installed` is `false` (or the block is absent), `ask-grok.sh` exits **3** with routing guidance instead of failing obscurely. Claude on such a machine should NOT attempt local Grok. +- **Current fleet Grok host: `GURU-5070`** — the only machine with Grok installed right now. When others get it, set their `identity.json` `grok` block (and update this line). + +**Remote routing (NOT yet wired):** a non-host machine cannot run Grok locally. To fulfill a Grok request from elsewhere, route it to the host (`GURU-5070`). Candidate channels: GuruRMM agent command execution (`/rmm` — GURU-5070 is enrolled; the hard part is shipping image/video artifacts back), `grok agent serve` (WebSocket relay), or a coord-API job queue. Until that's built, Grok requests originate on the host machine. + +## When to route to Grok + +- **Image / video creation or editing** — Claude can't generate media; Grok can. (`image`, `video`) +- **Current / real-time facts** — anything past Claude's knowledge cutoff, breaking news, latest versions, or X/Twitter sentiment. (`xsearch`) +- **Independent verification** — a genuinely different vendor/model to red-team a Claude finding or design before acting on it. (`verify`) +- **Diverse drafts / second opinion** — alternative phrasing or approach to compare. (`text`) + +## When NOT to + +- Pure classify/extract/summarize → cheaper via Tier-0 Ollama (`.claude/OLLAMA.md`). +- Editing this repo's code → Claude's own agents (the Grok CLI *can* read `.claude/` and run tools, but Claude owns the codebase work). +- **Never** delegate unsupervised destructive / production actions to Grok. The + earlier SBS post-mortem (`docs/session-notes/2026-06-03-claude-postmortem-grok-mspbackups-sbs.md`) + showed Grok over-claims and under-verifies — **always review Grok output before + acting on it**, and confirm media is what was asked for (Claude can view images). + +## Safety / operational notes + +- `~/.grok/config.toml` defaults to `permission_mode = "always-approve"` (auto-runs tools). The wrapper overrides with `--permission-mode dontAsk` and `--no-subagents`; do not bypass that. +- Prompts are passed via `--prompt-file` only (inline args break on shell quoting). +- `grok-build` rejects `--effort`/`--reasoning-effort` (400) — don't pass them. +- Models available: `grok-build` (default), `grok-composer-2.5-fast`. The agent self-reports as Grok 4.3. +- After media gen, Claude should **view the image** (Read tool) to confirm correctness; videos can be confirmed by header/ffprobe. + +## Reference +- Binary: `~/.grok/bin/grok.exe` (not on PATH; the wrapper auto-locates it or honors `GROK=`). +- Full capability investigation + verification: see the 2026-06-04 session log. +- Grok native tools observed: `image_gen`, `image_edit`, `image_to_video`, `reference_to_video`, `web_search`, `web_fetch`, `x_keyword_search`, `x_semantic_search`, `x_user_search`, `x_thread_fetch`, `run_terminal_command`, file ops, `scheduler_*`, `monitor`, memory. diff --git a/.claude/skills/grok/scripts/ask-grok.sh b/.claude/skills/grok/scripts/ask-grok.sh new file mode 100644 index 0000000..782660d --- /dev/null +++ b/.claude/skills/grok/scripts/ask-grok.sh @@ -0,0 +1,144 @@ +#!/usr/bin/env bash +# ask-grok.sh — Claude -> Grok capability router. +# +# Routes a task to the Grok CLI (xAI Grok 4.3) for capabilities Claude lacks +# (image/video generation, live web + X/Twitter data) or for an independent +# second model (verification, drafts). Headless, safe-by-default, artifact-aware. +# +# Auth is Grok's own OIDC (~/.grok/auth.json, grok.com login) — NO API key here. +# Prompts are ALWAYS passed via --prompt-file (inline args break on shell quoting). +# Artifacts (image/video) are retrieved by globbing the session dir by sessionId, +# so they're recovered even when the headless run reports stopReason=Cancelled +# before echoing the path (a known finalization quirk). +# +# Usage: +# ask-grok.sh text "" # text / reasoning / second opinion +# ask-grok.sh verify "" # adversarial check (text + --check) +# ask-grok.sh image "" [out.png] # image_gen -> copy artifact to out +# ask-grok.sh video "" [out.mp4] # image_to_video on input image +# ask-grok.sh xsearch "" # live X/Twitter + web search +# ask-grok.sh raw # escape hatch (passes through) +# +# Exit: 0 ok, 1 no result/artifact, 2 usage, 127 grok not found. +set -uo pipefail +SELF="ask-grok" + +PY="$(command -v py 2>/dev/null || command -v python 2>/dev/null || command -v python3 2>/dev/null || true)" +[ -z "$PY" ] && { echo "[$SELF] python (py/python/python3) required for JSON parsing" >&2; exit 127; } + +# --- identity.json (per-machine, gitignored) declares whether grok is installed here --- +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" 2>/dev/null && pwd)" +IDFILE="" +[ -n "${CLAUDETOOLS_ROOT:-}" ] && [ -f "$CLAUDETOOLS_ROOT/.claude/identity.json" ] && IDFILE="$CLAUDETOOLS_ROOT/.claude/identity.json" +[ -z "$IDFILE" ] && IDFILE="$(cd "$SCRIPT_DIR/../../.." 2>/dev/null && pwd)/identity.json" +idgrok() { # read field $1 from identity.json .grok (empty if absent) + [ -f "$IDFILE" ] || { echo ""; return; } + "$PY" -c "import json,sys +try: + g=(json.load(sys.stdin).get('grok') or {}); v=g.get('$1','') + print('' if v is None else (str(v).lower() if isinstance(v,bool) else v)) +except Exception: print('')" < "$IDFILE" +} + +# If identity explicitly says grok is NOT installed here, fail fast with routing guidance. +if [ "$(idgrok installed)" = "false" ]; then + echo "[$SELF] grok is not installed on this machine (identity.json grok.installed=false)." >&2 + echo "[$SELF] Grok runs only on the fleet grok host. Route this request there (remote routing not yet wired) or install grok + set identity.json grok.installed=true." >&2 + exit 3 +fi + +# --- locate the grok binary: GROK env > identity.json grok.binary > auto-locate --- +GROK="${GROK:-}" +cand="$(idgrok binary)" +[ -z "$GROK" ] && [ -n "$cand" ] && [ -x "$cand" ] && GROK="$cand" +if [ -z "$GROK" ]; then + if command -v grok >/dev/null 2>&1; then GROK="$(command -v grok)"; else + for c in "$HOME/.grok/bin/grok.exe" "/c/Users/${USERNAME:-${USER:-x}}/.grok/bin/grok.exe" \ + "$HOME/.grok/bin/grok" "${LOCALAPPDATA:-}/Programs/grok/grok.exe"; do + [ -x "$c" ] && { GROK="$c"; break; } + done + fi +fi +[ -z "$GROK" ] && { echo "[$SELF] grok CLI not found (set identity.json grok.binary, GROK=, or install grok)" >&2; exit 127; } + +MODE="${1:-}"; shift 2>/dev/null || true +[ -z "$MODE" ] && { echo "usage: $SELF {text|verify|image|video|xsearch|raw} ..." >&2; exit 2; } + +TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT +WORK="$TMP/work"; mkdir -p "$WORK" +PF="$TMP/prompt.txt"; OUT="$TMP/out.json" + +# run grok headless. $1=timeout secs; rest=extra flags. Reads $PF -> $OUT. +# Never fails the script on grok's exit code (Cancelled is expected; we read artifacts). +run_grok() { + local to="$1"; shift + timeout "$to" "$GROK" --prompt-file "$PF" --output-format json \ + --permission-mode dontAsk --no-subagents --no-plan --cwd "$WORK" "$@" \ + >"$OUT" 2>"$TMP/err.txt" || true +} + +# parse a top-level field from $OUT. Read via stdin so Windows python never has +# to resolve the git-bash (/c/...) path itself. +jfield() { "$PY" -c "import json,sys +try: + d=json.load(sys.stdin); print(d.get('$1','') or '') +except Exception: + print('')" < "$OUT"; } + +# newest artifact under any session dir for this sessionId: $1=sid $2=images|videos +find_artifact() { + ls -t "$HOME/.grok/sessions/"*"/$1/$2/"* 2>/dev/null | head -1 +} + +case "$MODE" in + text|verify) + [ -z "${1:-}" ] && { echo "usage: $SELF $MODE \"\"" >&2; exit 2; } + # Prompt-level steering keeps it text-only and (for verify) adversarial. + # (The --disallowed-tools/--rules flags tripped the CLI; --check adds a slow + # multi-turn self-check loop — both avoided in favor of prompt steering.) + if [ "$MODE" = "verify" ]; then + printf 'Adversarially evaluate the following claim/finding: try hard to find any reason it is WRONG. Then give a one-line verdict (CONFIRMED or REFUTED) plus a one-sentence justification. Answer in text only; do not use tools. Claim/finding: %s' "$1" > "$PF" + else + printf 'Answer directly in text; do not use tools. %s' "$1" > "$PF" + fi + run_grok 120 --disable-web-search --max-turns 2 + txt="$(jfield text)" + if [ -n "$txt" ]; then printf '%s\n' "$txt"; else + echo "[$SELF] no text (stopReason=$(jfield stopReason)); raw: $OUT" >&2; exit 1; fi + ;; + image) + [ -z "${1:-}" ] && { echo "usage: $SELF image \"\" [out.png]" >&2; exit 2; } + out="${2:-grok-image.png}" + printf 'Use your image_gen tool to generate exactly this image, save it, then stop. Image: %s' "$1" > "$PF" + run_grok 240 --disable-web-search --max-turns 12 + sid="$(jfield sessionId)"; art="$(find_artifact "$sid" images)" + if [ -n "$art" ] && [ -f "$art" ]; then cp -f "$art" "$out" + echo "[$SELF] image OK -> $out (session $sid)" + else echo "[$SELF] no image artifact (session=$sid, stopReason=$(jfield stopReason))" >&2; exit 1; fi + ;; + video) + [ -z "${1:-}" ] || [ -z "${2:-}" ] && { echo "usage: $SELF video \"\" [out.mp4]" >&2; exit 2; } + input="$2"; out="${3:-grok-video.mp4}" + [ -f "$input" ] || { echo "[$SELF] input image not found: $input" >&2; exit 2; } + cp -f "$input" "$WORK/input.jpg" + printf 'Use your image_to_video tool to animate input.jpg (in the current directory) into a short clip, save it, then stop. Motion: %s' "$1" > "$PF" + run_grok 360 --disable-web-search --max-turns 20 + sid="$(jfield sessionId)"; art="$(find_artifact "$sid" videos)" + if [ -n "$art" ] && [ -f "$art" ]; then cp -f "$art" "$out" + echo "[$SELF] video OK -> $out (session $sid)" + else echo "[$SELF] no video artifact (session=$sid, stopReason=$(jfield stopReason))" >&2; exit 1; fi + ;; + xsearch) + [ -z "${1:-}" ] && { echo "usage: $SELF xsearch \"\"" >&2; exit 2; } + printf 'Use your web_search and X/Twitter search tools to answer this, cite sources, then stop: %s' "$1" > "$PF" + run_grok 150 --max-turns 6 + txt="$(jfield text)" + if [ -n "$txt" ]; then printf '%s\n' "$txt"; else + echo "[$SELF] no result (stopReason=$(jfield stopReason))" >&2; exit 1; fi + ;; + raw) + "$GROK" "$@" + ;; + *) + echo "[$SELF] unknown mode '$MODE' (use text|verify|image|video|xsearch|raw)" >&2; exit 2 ;; +esac diff --git a/session-logs/2026-06-04-session.md b/session-logs/2026-06-04-session.md index 12d4d13..8aad165 100644 --- a/session-logs/2026-06-04-session.md +++ b/session-logs/2026-06-04-session.md @@ -65,3 +65,46 @@ None discovered or created this session. (Prior-day note still stands: the MSP36 - Commits: gururmm `9671c4b` (top-5), `d2d10e3` (size sweep); ClaudeTools `8fb45e4`, `13f992c`; build SHAs `fa901e28` (v0.2.37), `fc76b7fb` (v0.2.38). - Human-flow skill: `.claude/skills/human-flow/` (scanner `scripts/scan.mjs`, heuristics `references/mouse-keyboard-heuristics.md`). - Prior-day session log: `session-logs/2026-06-03-session.md` (beta channel + Grok post-mortem + MSP360/B2 cleanup). + +## Update: 07:59 PDT — Grok CLI integration (capability router + per-machine flag) + +### Summary +Explored and built Claude->Grok delegation using the locally-installed Grok CLI (`grok.exe`, xAI, "Grok Build TUI" 0.2.20). Discovered the binary at `C:\Users\guru\.grok\bin\grok.exe` (not on PATH; OIDC/grok.com login at `~/.grok/auth.json`, ~6h refresh, team account mike@azcomputerguru.com — NO developer API key). Learned the CLI surface (`--prompt-file`, `--output-format json`, `-p/--single`, `agent` subcommand, scoping flags). `grok inspect` showed Grok natively reads the Claude harness here (CLAUDE.md, settings.local.json 69 perms, all 52 skills). + +Per Mike's suggestion, interviewed the Grok agent itself (headless) — its leaked system-prompt tool list overturned an earlier wrong conclusion (I'd said the CLI was text-only and needed an API key). It has native media tools. VERIFIED live, no API key: `image_gen` produced a correct 1024x1024 red-circle JPG; `image_to_video` produced a 6.04s 544x544 H.264+AAC MP4 from it. Also has `web_search`/`web_fetch` + X/Twitter tools (xsearch returned the current Rust version 1.96.0 with x.com citations). Artifacts land in `~/.grok/sessions///{images,videos}/`. + +Built the `/grok` capability-router skill: `.claude/skills/grok/SKILL.md` + `scripts/ask-grok.sh` (modes: text, verify, image, video, xsearch, raw). All five modes tested working end-to-end. Then added a per-machine availability flag: `identity.json` `grok` block, with `migrate-identity.sh` auto-detecting Grok so every fleet machine self-populates `grok.installed` (false where absent); the wrapper is identity-aware (exits 3 with routing guidance on non-Grok machines). Remote routing (so other fleet machines could use this host's Grok) was DEFERRED. + +### Key Decisions +- Grok = capability EXTENSION (image/video, live web+X data, independent second model), not a replacement for Claude's coding/editing. Pure classify/extract stays on Tier-0 Ollama; repo code stays with Claude's agents. +- Prompts to grok ALWAYS via `--prompt-file` (inline args break on shell quoting). Parse grok JSON via stdin (Windows `py` can't open git-bash `/c/...` paths). Retrieve media artifacts by sessionId glob (robust against the headless `stopReason: Cancelled` finalization quirk). Override the config's `permission_mode = "always-approve"` with `--permission-mode dontAsk --no-subagents`. +- Per-machine flag lives in `identity.json` (gitignored, local); auto-detected by `migrate-identity.sh` (preserves manual `is_fleet_host`). Fleet host advertised in SKILL.md (currently GURU-5070, the only install). +- Deferred remote routing: only this workstation drives Claude+Grok now; the relay (esp. image/video artifact transport back to a requesting machine) isn't worth building yet. Per the SBS post-mortem, never delegate unsupervised destructive work to Grok; always review its output. + +### Problems Encountered +- `grok` / `agent` not on PATH -> located the binary by filesystem search. +- `--effort high` -> 400 "grok-build does not support reasoningEffort"; dropped it. +- Headless `-p` reported `Cancelled` after generating media but before echoing the path -> solved by retrieving artifacts via sessionId glob of the session dir. +- ask-grok.sh bugs found via testing: `set -u` tripped on unbound `$USER` (guarded); Windows `py` couldn't open the `/c/...` json path (switched to stdin); `--disallowed-tools`/`--rules`/`--check` tripped/slowed the CLI (replaced with prompt-level steering). All fixed; modes re-verified. + +### Configuration Changes +- NEW `.claude/skills/grok/SKILL.md`, `.claude/skills/grok/scripts/ask-grok.sh` +- EDIT `.claude/scripts/migrate-identity.sh` (Grok auto-detect -> identity.json `grok` block) +- EDIT `.claude/identity.json` (added `grok` block; gitignored/local — does NOT sync) + +### Commands & Outputs +- Headless text: `grok --prompt-file --output-format json` -> `{"text":"...","sessionId":"..."}`. +- Router: `bash .claude/skills/grok/scripts/ask-grok.sh {text|verify|image|video|xsearch} ...`. +- Verified: image -> 1024x1024 JPEG; video -> 6.04s 544x544 H.264 MP4 (ffprobe); xsearch -> live Rust 1.96.0 + citations. +- `migrate-identity.sh` -> "Grok: installed (C:/Users/guru/.grok/bin/grok.exe)", grok.installed: true, is_fleet_host preserved. + +### Pending / Incomplete Tasks +- Remote routing DEFERRED (candidates: GuruRMM agent command / `grok agent serve` / coord job queue; artifact transport is the hard part). +- Existing fleet machines should re-run `migrate-identity.sh` to populate `grok.installed` (else they hit a generic "grok not found" instead of the exit-3 routing message). Optional: have `/self-check` flag a missing `grok` block. +- Untested Grok tools: `image_edit`, `reference_to_video`. Optional `/grok` slash-command alias. +- Carried over: human-flow promote beta->prod when ready; MSP360/B2 follow-ups (coord todos 2e50f388 / dc3a6233 / 0fed5eb2); SBS portal removal (db03f8fe); rotate leaked MSP360 API key. + +### Reference Information +- Grok binary `~/.grok/bin/grok.exe` (0.2.20); auth `~/.grok/auth.json` (OIDC); config `~/.grok/config.toml` (`permission_mode=always-approve`, model grok-build). Models: grok-build (default), grok-composer-2.5-fast; agent self-reports Grok 4.3. +- Native tools: image_gen, image_edit, image_to_video, reference_to_video, web_search, web_fetch, x_keyword_search, x_semantic_search, x_user_search, x_thread_fetch, run_terminal_command, file ops, scheduler_*, monitor, memory. +- Skill: `.claude/skills/grok/`. Fleet Grok host: GURU-5070 (only install).