From 470e0ec2ddbbabe1b6274511f156729a76044747 Mon Sep 17 00:00:00 2001 From: andyb Date: Sat, 27 Jun 2026 11:22:48 -0500 Subject: [PATCH 1/6] bugfix(onboarding): harden .envrc adoption, prune broken VS Code extensions, quiet system_profiler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From the Mac Mini provisioning review (WR-18897): - onboard.sh: add envrc_is_sourceable() so a malformed/old-template .envrc is backed up and regenerated instead of silently adopted — the bug that skipped the prompt and spammed "command not found" on every new shell. Prompt logic extracted into generate_envrc(). - extensions_base.txt: remove GitHub.copilot-chat (now a built-in extension, install conflicts), stkb.rewrap (fails marketplace signature verification), and ms-vscode-remote.remote-wsl (Windows-only, no-op on macOS). - starship.zsh: redirect system_profiler stderr to /dev/null so its hw.cpufamily diagnostic stops leaking into every new shell. Co-Authored-By: Claude Opus 4.8 (1M context) --- engineering/ide/.vscode/extensions_base.txt | 3 -- onboarding_bin/onboard.sh | 58 +++++++++++++++++---- shell/starship.zsh | 7 ++- 3 files changed, 52 insertions(+), 16 deletions(-) diff --git a/engineering/ide/.vscode/extensions_base.txt b/engineering/ide/.vscode/extensions_base.txt index 4e92985..29bd867 100644 --- a/engineering/ide/.vscode/extensions_base.txt +++ b/engineering/ide/.vscode/extensions_base.txt @@ -9,7 +9,6 @@ esbenp.prettier-vscode foxundermoon.shell-format GitHub.codespaces GitHub.copilot -GitHub.copilot-chat github.vscode-github-actions GitHub.vscode-pull-request-github HTMLHint.vscode-htmlhint @@ -24,7 +23,6 @@ ms-playwright.playwright ms-vscode-remote.remote-containers ms-vscode-remote.remote-ssh ms-vscode-remote.remote-ssh-edit -ms-vscode-remote.remote-wsl ms-vscode-remote.vscode-remote-extensionpack ms-vscode.remote-explorer ms-vscode.remote-server @@ -36,7 +34,6 @@ richie5um2.vscode-sort-json ritwickdey.LiveServer ryu1kn.partial-diff steoates.autoimport -stkb.rewrap streetsidesoftware.code-spell-checker stylelint.vscode-stylelint timonwong.shellcheck diff --git a/onboarding_bin/onboard.sh b/onboarding_bin/onboard.sh index e8fc9b8..096a641 100755 --- a/onboarding_bin/onboard.sh +++ b/onboarding_bin/onboard.sh @@ -196,17 +196,23 @@ resolve_repo() { # ---------------------------------------------------------------------------- # Step 6 — Machine variables (.envrc). GATE only on first run (fill values). # ---------------------------------------------------------------------------- -ensure_envrc() { - step "Machine variables (.envrc)" - local target="$REPO_DIR/.envrc" +# True when $1 sources cleanly. A malformed old-template file (e.g. a line like +# `VAR=# Your key here` — `VAR=#` then `Your` is parsed as a command) emits +# "command not found" to stderr; empty stderr means the file is safe to adopt. +# Sourced in a throwaway subshell so it can't pollute our environment. +envrc_is_sourceable() { + [[ -f "$1" ]] || return 1 + local errout + errout="$(bash -c "source '$1'" 2>&1 >/dev/null)" || true + [[ -z "$errout" ]] +} + +# Prompt for machine variables and write a clean .envrc to $1. +generate_envrc() { + local target="$1" local pre="$REPO_DIR/onboarding_bin/pre-onboarding-script.sh" - if [[ -f "$target" ]]; then - ok ".envrc already present in repo" - elif [[ -f "$HOME/.envrc" ]]; then - mv "$HOME/.envrc" "$target" - ok "Moved ~/.envrc into the repo" - elif [[ -f "$pre" ]]; then + if [[ -f "$pre" ]]; then # Delegate to pre-onboarding-script.sh: it prompts for each value and writes # a clean, `export`-style ~/.envrc (no malformed placeholders). ONBOARD_ORCHESTRATED # tells it to skip its brew/clone tail (onboard.sh already did both). We feed @@ -217,11 +223,11 @@ ensure_envrc() { else ONBOARD_ORCHESTRATED=1 bash "$pre" || warn "pre-onboarding-script.sh reported errors (review above)" fi - if [[ -f "$HOME/.envrc" ]]; then + if [[ -f "$HOME/.envrc" ]] && envrc_is_sourceable "$HOME/.envrc"; then mv "$HOME/.envrc" "$target" ok ".envrc generated and saved into the repo" else - warn "~/.envrc was not created — falling back to the template" + warn "~/.envrc was not created cleanly — falling back to the template" cp "$REPO_DIR/.envrc.example" "$target" "${EDITOR:-open}" "$target" >/dev/null 2>&1 || open "$target" 2>/dev/null || true ask _ " Press Enter once you've saved your values in .envrc... " @@ -233,6 +239,36 @@ ensure_envrc() { ask _ " Press Enter once you've saved your values in .envrc... " ok ".envrc ready" fi +} + +ensure_envrc() { + step "Machine variables (.envrc)" + local target="$REPO_DIR/.envrc" + + # Only adopt an existing .envrc if it actually sources cleanly. Otherwise an + # old-template copy (malformed `VAR=# ...` lines, placeholder values) gets + # silently adopted, the prompt is skipped, and every new shell spews + # "command not found" while aliases resolve to bogus placeholder paths. + if [[ -f "$target" ]]; then + if envrc_is_sourceable "$target"; then + ok ".envrc already present in repo" + else + warn "Repo .envrc is malformed (old template?) — backing up to .envrc.malformed.bak and regenerating" + mv "$target" "$target.malformed.bak" + generate_envrc "$target" + fi + elif [[ -f "$HOME/.envrc" ]]; then + if envrc_is_sourceable "$HOME/.envrc"; then + mv "$HOME/.envrc" "$target" + ok "Moved ~/.envrc into the repo" + else + warn "Existing ~/.envrc is malformed (old template?) — backing up to ~/.envrc.malformed.bak and regenerating" + mv "$HOME/.envrc" "$HOME/.envrc.malformed.bak" + generate_envrc "$target" + fi + else + generate_envrc "$target" + fi # Load values into THIS session so later steps (company path, keys) see them. set -a; # shellcheck disable=SC1090 diff --git a/shell/starship.zsh b/shell/starship.zsh index 912e252..db62373 100644 --- a/shell/starship.zsh +++ b/shell/starship.zsh @@ -6,8 +6,11 @@ if [[ -f $LFILE ]]; then elif [[ -f $MFILE ]]; then _distro="macos" - # on mac os use the systemprofiler to determine the current model - _device=$(system_profiler SPHardwareDataType | awk '/Model Name/ {print $3,$4,$5,$6,$7}') + # on mac os use the systemprofiler to determine the current model. + # 2>/dev/null: on some Macs system_profiler prints a diagnostic line + # (e.g. "hw.cpufamily: 0x...") to stderr that would otherwise leak into + # every new shell. We only want stdout ("Model Name") for the icon match. + _device=$(system_profiler SPHardwareDataType 2>/dev/null | awk '/Model Name/ {print $3,$4,$5,$6,$7}') case $_device in *MacBook*) DEVICE="" ;; From 1a1555bb5089cf88ccfd84f9d49b0cafc9b2ed68 Mon Sep 17 00:00:00 2001 From: andyb Date: Sat, 27 Jun 2026 13:08:09 -0500 Subject: [PATCH 2/6] bugfix(onboarding): collect machine vars before clone to fix CURRENT_COMPANY path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit resolve_repo used CURRENT_COMPANY from the calling shell's environment to decide the clone path, then ensure_envrc prompted the user for the real value — too late. If the calling shell had a stale CURRENT_COMPANY (e.g. from a prior job), the repo landed in the wrong directory and every alias derived from it resolved to a corrupt path. Fix: introduce collect_machine_vars, which runs before resolve_repo. It sources any existing valid .envrc (fast no-op on re-runs) or prompts the user and writes ~/.envrc, then sources it so CURRENT_COMPANY is correct before any path decision is made. ensure_envrc (post-clone) then moves ~/.envrc into $REPO_DIR as before. Additional hardening: - CURRENT_COMPANY prompt default is hardcoded to "Retail-Success" (the correct value for this repo), so a stale env var can never silently win. - IDE_PATH defaults to "code" (VS Code CLI); stale "vscode" values are normalised at both init and post-prompt time. - generate_envrc checks ~/.envrc first to prevent double-prompting on the malformed-repo-.envrc edge case. - Generated .envrc now covers the full variable set from .envrc.example (adds JIRA_API_TOKEN placeholder and GIT_EMAIL_ADDRESS_PERSONAL). Co-Authored-By: Claude Sonnet 4.6 --- onboarding_bin/onboard.sh | 98 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/onboarding_bin/onboard.sh b/onboarding_bin/onboard.sh index 096a641..984cb7a 100755 --- a/onboarding_bin/onboard.sh +++ b/onboarding_bin/onboard.sh @@ -56,6 +56,96 @@ ask() { # ask [default] STACK="${1:-}" MANUAL_ACTIONS=() +# ---------------------------------------------------------------------------- +# Step 5a — Collect machine variables BEFORE resolve_repo so CURRENT_COMPANY +# is set correctly before the clone-path decision. On re-runs this +# is a no-op (sources the existing .envrc and returns immediately). +# Writes to ~/.envrc; ensure_envrc (post-clone) moves it into +# $REPO_DIR and sources it as the authoritative copy. +# ---------------------------------------------------------------------------- + +# Global scratch vars (NOT local) so ask()'s printf -v assignment propagates back. +_CMV_EMAIL="" _CMV_NAME="" _CMV_USER="" _CMV_GITHUB="" _CMV_COMPANY="" +_CMV_IDE="" _CMV_JIRA_URL="" _CMV_GPG="" _CMV_JIRA_EMAIL="" + +collect_machine_vars() { + # Running from inside the repo already? Source the repo's .envrc if valid. + local here; here="$(cd "$(dirname "${BASH_SOURCE[0]:-}")/.." 2>/dev/null && pwd)" + if [[ -n "$here" && -f "$here/install-profile" && -d "$here/meta" ]]; then + if [[ -f "$here/.envrc" ]] && envrc_is_sourceable "$here/.envrc"; then + set -a; . "$here/.envrc"; set +a + ok "Machine variables loaded from repo .envrc (re-run)" + return + fi + fi + + # ~/.envrc written by a prior bootstrap run? Adopt it. + if [[ -f "$HOME/.envrc" ]] && envrc_is_sourceable "$HOME/.envrc"; then + set -a; . "$HOME/.envrc"; set +a + ok "Machine variables loaded from existing ~/.envrc" + return + fi + + # First run: prompt, write ~/.envrc, source it so CURRENT_COMPANY is available + # to resolve_repo immediately below. + log "Collecting machine variables (needed before cloning the dotfiles repo)..." + + _CMV_EMAIL="${GIT_EMAIL_ADDRESS_PROFESSIONAL:-}" + _CMV_NAME="${CURRENT_NAME:-}" + _CMV_USER="${CURRENT_USER:-$USER}" + _CMV_GITHUB="${CURRENT_USER_GITHUB_URL:-https://github.com/$USER}" + # Hardcode the company default: ignoring any stale CURRENT_COMPANY in the calling + # shell is the whole point of this fix — "Retail-Success" is always the correct + # answer for this repo's onboarding. + _CMV_COMPANY="Retail-Success" + # Default to "code" (VS Code CLI); normalise any stale "vscode" value. + _CMV_IDE="${IDE_PATH:-code}" + [[ "$_CMV_IDE" == "vscode" ]] && _CMV_IDE="code" + _CMV_JIRA_URL="${JIRA_BASE_URL:-}" + _CMV_GPG="${CURRENT_USER_GPG_KEY:-}" + _CMV_JIRA_EMAIL="${JIRA_USER_EMAIL:-}" + + ask _CMV_EMAIL "Enter your git email [$_CMV_EMAIL]: " "$_CMV_EMAIL" + ask _CMV_NAME "Enter your name [$_CMV_NAME]: " "$_CMV_NAME" + ask _CMV_USER "Enter your username [$_CMV_USER]: " "$_CMV_USER" + ask _CMV_GITHUB "Enter your GitHub URL [$_CMV_GITHUB]: " "$_CMV_GITHUB" + ask _CMV_COMPANY "Enter your company name [$_CMV_COMPANY]: " "$_CMV_COMPANY" + ask _CMV_IDE "Enter your IDE command (e.g. 'code') [$_CMV_IDE]: " "$_CMV_IDE" + ask _CMV_JIRA_URL "Enter your JIRA Base URL [$_CMV_JIRA_URL]: " "$_CMV_JIRA_URL" + ask _CMV_GPG "Enter your GPG key id (blank = none) [$_CMV_GPG]: " "$_CMV_GPG" + ask _CMV_JIRA_EMAIL "Enter your JIRA user email [$_CMV_JIRA_EMAIL]: " "$_CMV_JIRA_EMAIL" + + # Normalise IDE_PATH in case the user typed "vscode". + [[ "$_CMV_IDE" == "vscode" ]] && _CMV_IDE="code" + + # Write ~/.envrc with the full variable set from .envrc.example (JIRA_API_TOKEN + # and GIT_EMAIL_ADDRESS_PERSONAL are intentionally left empty — set them manually + # after onboarding completes, or let the dotbot security step handle them). + cat > "$HOME/.envrc" < Date: Sun, 28 Jun 2026 11:35:34 -0500 Subject: [PATCH 3/6] chore(mac-mini): add Docker MCP Toolkit + Claude Code 24/7 bootstrap script Stands up the Docker MCP gateway (stdio, no url/auth) and wires Claude Code to it, replicating the default profile's enabled servers. Includes interactive secret setup and a reboot-survival checklist (auto-login, Docker autostart, sleep prevention, keychain). Co-Authored-By: Claude Opus 4.8 (1M context) --- mac-mini-agent/mac-mini-mcp-bootstrap.sh | 121 +++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100755 mac-mini-agent/mac-mini-mcp-bootstrap.sh diff --git a/mac-mini-agent/mac-mini-mcp-bootstrap.sh b/mac-mini-agent/mac-mini-mcp-bootstrap.sh new file mode 100755 index 0000000..f2fe1f0 --- /dev/null +++ b/mac-mini-agent/mac-mini-mcp-bootstrap.sh @@ -0,0 +1,121 @@ +#!/usr/bin/env bash +# ───────────────────────────────────────────────────────────────────────────── +# mac-mini-mcp-bootstrap.sh +# Stand up Docker MCP Toolkit + Claude Code on a 24/7 Mac mini. +# +# Replicates the "default" profile from andyb's primary machine. +# Run interactively the first time: bash mac-mini-mcp-bootstrap.sh +# +# Docker MCP Toolkit connects over LOCAL STDIO (`docker mcp gateway run`). +# There is NO url and NO auth token to configure — ignore any yaml template +# that asks for `url:` / `auth:`. That format is for remote HTTP/SSE servers. +# ───────────────────────────────────────────────────────────────────────────── +set -euo pipefail + +bold() { printf '\033[1m%s\033[0m\n' "$*"; } +step() { printf '\n\033[1;36m▶ %s\033[0m\n' "$*"; } +warn() { printf '\033[1;33m! %s\033[0m\n' "$*"; } + +# ── 0. Prereqs ─────────────────────────────────────────────────────────────── +step "0. Checking prerequisites" +command -v docker >/dev/null || { warn "Docker Desktop not installed. Install it first: https://docs.docker.com/desktop/setup/install/mac-install/"; exit 1; } +command -v claude >/dev/null || { warn "Claude Code CLI not on PATH. Install: npm i -g @anthropic-ai/claude-code (or the official installer)"; exit 1; } +docker mcp --version >/dev/null 2>&1 || { warn "'docker mcp' unavailable — enable MCP Toolkit in Docker Desktop > Settings > Beta features."; exit 1; } +bold " docker: $(docker --version)" +bold " claude: $(claude --version 2>/dev/null || echo present)" + +# ── 1. Make sure Docker is actually running ────────────────────────────────── +step "1. Verifying Docker daemon is up" +if ! docker info >/dev/null 2>&1; then + warn "Docker isn't running. Start Docker Desktop, wait for it to go green, then re-run." + open -a Docker || true + exit 1 +fi +bold " Docker daemon reachable ✔" + +# ── 2. Enable the servers (default profile) ────────────────────────────────── +# Mirrors the primary machine. Edit this list to taste. +step "2. Enabling MCP servers on the 'default' profile" +DEFAULT_SERVERS=( + atlassian + awslabs-cloudwatch + awslabs-cloudwatch-appsignals + context7 + fetch + filesystem + gemini-api-docs + git + github-chat + github-official + markdownify + memory + npm-sentinel + playwright + playwright-mcp-server + sentry-remote + sequentialthinking + youtube_transcript +) +docker mcp profile server add default "${DEFAULT_SERVERS[@]}" +bold " Enabled: ${DEFAULT_SERVERS[*]}" +echo +docker mcp profile server ls + +# ── 3. Secrets — these do NOT transfer from the other Mac ──────────────────── +# Secrets live in the macOS Keychain on each machine. Set them here once. +# Re-run any line as needed; value is read from stdin so it won't hit shell history. +step "3. Secrets (set the ones you use; skip with Ctrl-D)" +warn "These are stored in this Mac mini's keychain. They were NOT copied from your laptop." +set_secret() { + local key="$1" label="$2" + read -r -s -p " ${label} (Enter to skip): " val; echo + if [[ -n "${val}" ]]; then + printf '%s' "${val}" | docker mcp secret set "${key}" + bold " set ${key}" + else + echo " skipped ${key}" + fi +} +set_secret github.personal_access_token "GitHub PAT" +set_secret github-chat.api_key "github-chat API key" +set_secret atlassian.jira.api_token "Jira API token" +set_secret atlassian.jira.personal_token "Jira personal token" +set_secret atlassian.confluence.api_token "Confluence API token" +set_secret atlassian.confluence.personal_token "Confluence personal token" +echo +warn "OAuth-based servers (atlassian-remote, sentry-remote, github official OAuth) must be" +warn "re-authorized interactively on THIS machine: docker mcp oauth authorize " + +# ── 4. Connect Claude Code to the gateway (the easy way) ───────────────────── +step "4. Wiring Claude Code to the Docker MCP gateway" +docker mcp client connect claude-code +# Equivalent manual form (stdio, no url/auth): +# claude mcp add MCP_DOCKER -- docker mcp gateway run +bold " Connected. Verify with: claude mcp list" + +# ── 5. Make it survive reboots (mostly manual / GUI) ───────────────────────── +step "5. 24/7 survival checklist (do these once, by hand)" +cat <<'EOF' + Docker Desktop must be running for the gateway to work, and that needs a + logged-in GUI session. SSH alone is not enough. Configure: + + a) AUTO-LOGIN after reboot: + System Settings > Users & Groups > Automatic login > + (Disable FileVault, or auto-login won't run until you type the disk password.) + + b) DOCKER DESKTOP autostart: + Docker Desktop > Settings > General > + [x] Start Docker Desktop when you sign in + [x] (optional) Open Docker Dashboard at startup -> leave OFF for headless + + c) PREVENT SLEEP (so the box stays reachable 24/7): + sudo pmset -a sleep 0 disablesleep 1 womp 1 + sudo systemsetup -setcomputersleep Never + + d) KEYCHAIN: auto-login keeps the login keychain unlocked, which is what lets + `docker mcp secret` read your tokens unattended. If you lock the screen + manually the keychain stays unlocked; a full logout will re-lock it. +EOF + +step "Done." +bold "Sanity check: claude mcp list (look for: MCP_DOCKER: docker mcp gateway run - Connected)" From 8014d1bc14cece175fc2989bc021cfa5797a58cf Mon Sep 17 00:00:00 2001 From: andyb Date: Sun, 28 Jun 2026 11:41:45 -0500 Subject: [PATCH 4/6] maintenance(Brewfile): [WR-18897] Add 'ollama' AI CLI tool for enhanced development support. --- Brewfile.base | 1 + 1 file changed, 1 insertion(+) diff --git a/Brewfile.base b/Brewfile.base index 0397cc4..52e7c13 100755 --- a/Brewfile.base +++ b/Brewfile.base @@ -16,6 +16,7 @@ brew 'icu4c' # REQUIRED - BASE/ROR/NODE/DEVOPS/PYTHON Compiled Library D brew 'gnupg' # REQUIRED - Needed for signing your commits with github. brew 'mongosh' # RECOMMENDED - Commonly used with nosql projects. brew 'nvm' # REQUIRED - BASE/ROR/NODE/DEVOPS/PYTHON Version manager for node +brew 'ollama' # RECOMMENDED - AI Ollama CLI tool brew 'openssl' # REQUIRED - ROR/NODE/DEVOPS/PYTHON/PG Network Utility for SSL requests brew 'pandoc' # OPTIONAL - ROR/NODE/DEVOPS/PYTHON/PG Allows for markdown to easily exported to word, pdfs, & html brew 'pipx' # REQUIRED - BASE/ROR/NODE/DEVOPS/PYTHON Recommended package manager for python From 4ad3a20c96ef5b87db4cdc39aea8c81e9cb49554 Mon Sep 17 00:00:00 2001 From: andyb Date: Sun, 28 Jun 2026 11:41:49 -0500 Subject: [PATCH 5/6] maintenance(dotfiles): [WR-18897] Ensure ~/.local/bin is on PATH for Hermes Agent support. --- shell/.zprofile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shell/.zprofile b/shell/.zprofile index 7365d50..96ebef1 100644 --- a/shell/.zprofile +++ b/shell/.zprofile @@ -21,3 +21,6 @@ fi # Created by `pipx` on 2024-09-29 16:51:26 # export PATH="$PATH:/Users/acbarrows/.local/bin" + +# Hermes Agent — ensure ~/.local/bin is on PATH +export PATH="$HOME/.local/bin:$PATH" From 1f94fc58a5e6e434b25494ea78176a9eb22e996c Mon Sep 17 00:00:00 2001 From: andyb Date: Sun, 28 Jun 2026 14:26:45 -0500 Subject: [PATCH 6/6] maintenance(onboarding): [WR-18897] Enhanced onboarding scripts and documentation for Mac Mini setup. --- .claude/settings.local.json | 14 +- Brewfile.base | 1 - HANDOFF.md => ai/plans/HANDOFF.md | 0 .../plans/ONBOARDING-PLAN.md | 0 ai/workflows/mac-mini-agent/DRY-RUN-PLAN.md | 236 ++++++++++++++++++ .../mac-mini-agent}/mac-mini-mcp-bootstrap.sh | 0 6 files changed, 249 insertions(+), 2 deletions(-) rename HANDOFF.md => ai/plans/HANDOFF.md (100%) rename ONBOARDING-PLAN.md => ai/plans/ONBOARDING-PLAN.md (100%) create mode 100644 ai/workflows/mac-mini-agent/DRY-RUN-PLAN.md rename {mac-mini-agent => ai/workflows/mac-mini-agent}/mac-mini-mcp-bootstrap.sh (100%) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 9c92205..ef5a6de 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -12,7 +12,19 @@ "Bash(set -e)", "mcp__claude_ai_Atlassian__getAccessibleAtlassianResources", "mcp__claude_ai_Atlassian__getJiraProjectIssueTypesMetadata", - "mcp__claude_ai_Atlassian__createJiraIssue" + "mcp__claude_ai_Atlassian__createJiraIssue", + "mcp__claude_ai_Atlassian__getVisibleJiraProjects", + "mcp__claude_ai_Atlassian__searchJiraIssuesUsingJql", + "Bash(awk '/^main\\\\\\(\\\\\\)|^# ===.*Main|^run\\\\\\(\\\\\\)|^# Orchestrat/{f=1} f' onboarding_bin/onboard.sh)", + "Bash(bash -n /Users/andyb/Retail-Success/repos/development-team/dotfiles/onboarding_bin/onboard.sh)", + "Bash(find \"/Users/andyb/Library/Application Support/Claude/local-agent-mode-sessions/skills-plugin\" -name \"SKILL.md\" 2>/dev/null | grep -E \"local-development|developer-onboard\" | head -10)", + "Bash(find \"/Users/andyb/Library/Application Support/Claude\" -name \"SKILL.md\" 2>/dev/null | xargs grep -l -E \"local.development|developer.onboard|onboard\" 2>/dev/null | head -20)", + "Bash(find ~/.claude/plugins/cache -name \"SKILL.md\" 2>/dev/null | xargs grep -l -i \"local.develop\\\\|developer.onboard\\\\|onboard\\\\|machine.setup\" 2>/dev/null)", + "Bash(brew info *)", + "Bash(brew tap *)", + "Bash(/opt/homebrew/bin/hermes --version)", + "Read(//Users/andyb/.local/bin/**)", + "Read(//Users/andyb/.hermes/**)" ] } } diff --git a/Brewfile.base b/Brewfile.base index 52e7c13..925a830 100755 --- a/Brewfile.base +++ b/Brewfile.base @@ -11,7 +11,6 @@ brew 'freetype' # REQUIRED - BASE/ROR/NODE CL brew 'gh' # REQUIRED - BASE/ROR/NODE/DEVOPS Github CLI brew 'git' # REQUIRED - ROR/NODE/DEVOPS/PYTHON Core version control brew 'hadolint' # REQUIRED - BASE/ROR/NODE/DEVOPS/PYTHON Used to lint Dockerfiles../ -brew 'hermes-agent' # REQUIRED - Coding harness and agent that self learns. brew 'icu4c' # REQUIRED - BASE/ROR/NODE/DEVOPS/PYTHON Compiled Library Dependency for unicode and globalization brew 'gnupg' # REQUIRED - Needed for signing your commits with github. brew 'mongosh' # RECOMMENDED - Commonly used with nosql projects. diff --git a/HANDOFF.md b/ai/plans/HANDOFF.md similarity index 100% rename from HANDOFF.md rename to ai/plans/HANDOFF.md diff --git a/ONBOARDING-PLAN.md b/ai/plans/ONBOARDING-PLAN.md similarity index 100% rename from ONBOARDING-PLAN.md rename to ai/plans/ONBOARDING-PLAN.md diff --git a/ai/workflows/mac-mini-agent/DRY-RUN-PLAN.md b/ai/workflows/mac-mini-agent/DRY-RUN-PLAN.md new file mode 100644 index 0000000..f23ed1f --- /dev/null +++ b/ai/workflows/mac-mini-agent/DRY-RUN-PLAN.md @@ -0,0 +1,236 @@ +# Local‑AI Agent Host — Dry‑Run Plan + +**Author:** Claude (Opus 4.8) for @andyb · **Date:** 2026‑06‑28 +**Dry‑run target:** `rss-mbp-5` (your MacBook — daily driver) · **Promotion target:** the mac‑mini (24/7 box) +**Status:** PLAN — nothing executed yet. Uncommitted. + +> **Thesis.** Across your 06‑19 → 06‑28 sessions you've been building toward one thing from +> several angles: **a mac‑mini that runs your full AI tooling as an always‑on agent.** The pieces +> already exist but were assembled in separate conversations and never wired together: +> the dotfiles `onboard.sh` *installs* everything, `mac-mini-mcp-bootstrap.sh` *wires the tools (MCP)*, +> the `wayroo-demo` work *proved the always‑on pattern (init‑check → launchd → health probe)*, and +> Ollama + Hermes are *installed but unconfigured*. **"Next level" = unify those fragments into one +> coherent, reversible flow.** **"Dry run" = rehearse that whole flow ephemerally on `rss-mbp-5` +> (stand up → validate → tear down) before it touches the mini or production `onboard.sh`.** + +--- + +## 1. Objectives — recovered from repeated mentions (with evidence) + +Each objective below is something you said **more than once, across more than one session**. Frequency +and a representative quote are cited so you can audit the recovery. Where the transcripts left a gap, +the choice is explicitly marked **[my recommendation, not your stated requirement]**. + +| # | Objective | Evidence (sessions) | +|---|-----------|---------------------| +| **O1** | **Provision the mac‑mini as a 24/7 always‑on agent box** running your full AI tooling + plugins. *(most‑repeated)* | `"We have a macmini that we are provisioning to be an always on bot that can leverage our full AI tooling, integrations, and those plugins."` — 06‑23 (repeated); 06‑28 `"the mac-mini that will be used for 24/7 agent use"` | +| **O2** | **A clean, idempotent, reversible one‑liner dotfiles onboarding** (`onboard.sh`). Hardened every session: `.env` prompts must run **before** dir creation; alias/`.envrc` pathing; VS Code symlinks; brew recipes. | 06‑19, 06‑22, 06‑26, 06‑28 (branch `feature/WR-18897-onboarding-dotfiles-hardening`) | +| **O3** | **Add Ollama + local models** to the stack. | 06‑28 `/compact`: `"add in using ollama and being able to use local models"` — installed via `Brewfile.base` line 19, never configured | +| **O4** | **Hermes Agent** as the self‑improving coding agent, routing between **local (Ollama)** and **cloud** models. | `Brewfile.base` line 14 `hermes-agent` "Coding harness and agent that self learns"; 06‑27 commit `4b88178`; raised on the mini 06‑28 | +| **O5** | **rs‑agents plugin as the brain** of the box — `init-check` gate → launchd always‑on → health/registry probe. | 06‑23 `wayroo-demo`: registry, `init-check`, launchd units (`RunAtLoad`/`KeepAlive`), health probe on :8765 | +| **O6** | **Fold learnings back into rs‑agents** skills/knowledge (`local-development-setup`, `developer-onboarding`) + maybe a new skill/workflow. | 06‑28 (verbatim): `"Use the /rs-agents:rs-agents and the /skill-creator to update /local-development-setup and/or the /developer-onboarding skill. From our learnings today, extract any wisdom we've gained..."` | +| **O7** | **Safety throughout** — idempotent, re‑runnable, reversible, dry‑run‑able. | `onboard.sh` "Re‑running is safe"; `ONBOARDING-PLAN.md` "Treat the Mini as the test bed" | + +**Resolved open question:** "hermes" — unclarified in the 06‑28 Luci session — is **Hermes Agent by Nous +Research** (`hermes-agent.nousresearch.com`): *"a self‑improving AI agent that creates skills from +experience."* Its CLI (`model` / `moa` / `fallback` / `proxy` / `mcp` / `skills`) is what makes O3+O4 one +architecture, not two. + +--- + +## 2. How the pieces fit (the architecture you're actually building) + +``` + ┌─────────────────────────────────────────────┐ + │ rs-agents plugin (the "team"/brain) │ + │ orchestrator → specialist sub-agents │ + │ gate: init-check · always-on: launchd │ + └───────────────┬─────────────────────────────┘ + │ uses + ┌────────────────────────────┼────────────────────────────┐ + │ MODEL/AGENT RUNTIME PLANE │ TOOL PLANE │ + ▼ ▼ ▼ + ┌──────────────┐ ┌────────────────────┐ ┌────────────────────┐ + │ Ollama │◀──/v1──────│ Hermes Agent │ │ Docker MCP Toolkit │ + │ local models │ OpenAI- │ routes local+cloud │ │ 17 MCP servers │ + │ :11434 │ compat │ fallback · moa │ │ gateway (stdio) │ + └──────────────┘ └────────────────────┘ └────────────────────┘ + ▲ ▲ ▲ + └─────────────── installed by ── dotfiles onboard.sh / Brewfile ──┘ +``` + +- **Ollama** = local model **server** (OpenAI‑compatible at `:11434/v1`). Already installed. +- **Hermes** = agent **runtime** that points at Ollama for local inference, with a **fallback chain** to + cloud when local can't cope, and self‑improving **skills**. Already installed (note: brew `hermes` is + PATH‑shadowed by `~/.local/bin/hermes` — the pip build wins; that's the featureful one). +- **Docker MCP Toolkit** = the **tool plane** — already wired by `mac-mini-mcp-bootstrap.sh`. +- **rs‑agents** = the Claude‑Code **agent team**; `init-check` + launchd + health probe = the always‑on + pattern from `wayroo-demo`. +- **dotfiles `onboard.sh`** = installs all of the above on a fresh Mac. + +The **only missing wire** is the model/agent runtime plane (Ollama ↔ Hermes) and its always‑on form. +This plan stands that wire up — safely, on the laptop, first. + +--- + +## 3. THE DRY RUN on `rss-mbp-5` (ephemeral · reversible · the core deliverable) + +**Golden rule (daily‑driver safety):** the dry run installs **nothing persistent** — **no launchd units, +no `brew services`, no auto‑login, no sleep changes**. Everything runs in the foreground or in a sandbox +dir and is torn down by one command block. The persistence belongs to the mini (§6). + +Run inside a sandbox so nothing leaks into your real config: + +```bash +export DRY=~/.local/state/local-ai-dryrun # sandbox for logs/config +mkdir -p "$DRY" && cd "$DRY" +``` + +Each step is **COMMAND → OBSERVABLE PASS CRITERION**. If a step's criterion isn't visibly met, stop. + +### Step 0 — Preflight snapshot *(read‑only)* +```bash +for t in ollama hermes docker brew; do printf "%-8s %s\n" "$t" "$(command -v $t)"; done +ollama --version; hermes --version; docker --version +sysctl -n hw.memsize | awk '{print "RAM:", $1/1073741824 "GB"}' +``` +**PASS:** all four resolve; you note your RAM (drives model choice below). + +### Step 1 — Bring up Ollama ephemerally + pull one small model +```bash +ollama serve >"$DRY/ollama.log" 2>&1 & echo $! > "$DRY/ollama.pid" # foreground daemon, captured PID +sleep 2 +ollama pull llama3.2:3b # ~2GB smoke model — fast, proves the path +curl -s http://localhost:11434/api/tags | jq '.models[].name' +curl -s http://localhost:11434/api/generate -d '{"model":"llama3.2:3b","prompt":"reply with the single word: ready","stream":false}' | jq -r .response +``` +**PASS:** `/api/tags` lists the model **and** `/api/generate` returns text (≈ "ready"). +**Model choice [my recommendation, not your stated requirement — no model was named in any transcript]:** +`llama3.2:3b` for the smoke test (small/fast); `qwen2.5-coder:7b` (~4.7GB) as the real coding model once +the path works. Rule of thumb: keep the model ≤ ~60% of RAM. + +### Step 2 — Confirm Hermes routes to the LOCAL model ⚠️ *(the step to verify, not assume)* +Hermes consumes OpenAI‑compatible `/v1` providers; Ollama serves `http://localhost:11434/v1`. Confirm +Hermes can be pointed there (via `hermes model` picker with a custom `--inference-url`, a config entry, or +`-m … --provider …`). Then run one prompt and confirm it hit the **local** server: +```bash +: > "$DRY/ollama.log" # clear log so the next hit is unambiguous +hermes -z "Say hello in 5 words." -m llama3.2:3b --provider # exact flag TBD in spike +grep -i "POST /v1/chat/completions\|/api/chat\|llama3.2" "$DRY/ollama.log" # proof the local model served it +``` +**PASS:** Hermes returns a completion **and** `ollama.log` shows the request — i.e. inference was local. +**FALLBACK if Hermes won't point at Ollama directly:** use **LM Studio** (also in your Brewfile, serves +OpenAI‑compat) or call Ollama's `/v1` endpoint directly; either still satisfies O3. Record what worked — +that becomes the documented recipe (§5). + +### Step 3 — MCP tool plane sanity *(lightweight — skip secrets in the dry run)* +```bash +docker mcp --version && docker mcp server ls 2>/dev/null | head +``` +**PASS:** `docker mcp` responds and lists servers. (Full secrets/keychain wiring is a mini concern, §6 — +don't load real PATs on the laptop for a dry run.) + +### Step 4 — rs‑agents gate: does the plugin load and register? +Reuse the **`init-check`** you built in `wayroo-demo` (the "FIRST GATE — near‑free, no gateway"). Intent: +confirm the rs‑agents plugin loads and the agents/skills register. +```bash +# from wherever your init-check lives (wayroo-demo); confirm path during the spike +npm run init-check # expect: AGENTS non-empty (~25 agents, ~59 skills) +``` +**PASS:** registry reports the agent/skill roster non‑empty. If empty → the plugin didn't load; fix +`RS_AGENTS_PATH` before going further. + +### Step 5 — Health/registry probe (foreground, NOT launchd) +Run the demo server/health probe in the foreground and hit it once: +```bash +# foreground only for the dry run — no launchd, no KeepAlive +curl -s localhost:8765/health | jq . # adjust to your wayroo-demo probe +``` +**PASS:** health returns `healthy` with `maxConcurrency`/`waiting` populated. + +### Step 6 — TEARDOWN (single block — leaves the laptop exactly as found) +```bash +kill "$(cat "$DRY/ollama.pid")" 2>/dev/null # stop the ephemeral Ollama daemon +ollama rm llama3.2:3b 2>/dev/null # optional: reclaim ~2GB +# stop any foreground demo server (Ctrl-C in its terminal) +rm -rf "$DRY" # remove sandbox logs/config +# NOTE: nothing was added to launchd / brew services / login items — nothing to undo there. +``` +**PASS:** `pgrep ollama` empty, sandbox gone, no launchd/login changes exist. + +**Dry‑run definition of done:** Steps 1, 2, 4, 5 each hit their PASS criterion, and Step 6 returns the +machine to its prior state. That's a complete, observable rehearsal of the always‑on stack — minus the +"always‑on." + +--- + +## 4. Run the dry run *through* the rs‑agents plugin (O5/O6) + +You asked to **leverage the company rs‑agents plugin** — so don't run §3 by hand, drive it through the +orchestrator (and let it use the right specialists): + +``` +/rs-agents → "Execute the Local-AI Agent Host dry run in mac-mini-agent/DRY-RUN-PLAN.md §3 on this + MacBook, ephemerally. devops-engineer owns the bootstrap/teardown; verify each step's + observable PASS criterion before proceeding; do NOT install launchd/brew-services/login + items; report a per-step pass/fail table." +``` +- **devops-engineer** — runs/edits the bootstrap + teardown, owns Step 1–3, 5–6. +- **debugger** — if Step 2 (Hermes↔Ollama) doesn't route locally. +- **tech-writer / skill author** — captures the result into rs‑agents (§5). + +--- + +## 5. Capture learnings → rs‑agents (O6 — the "fold it back in" you asked for 06‑28) + +**Decision (your call):** **extend the two existing skills** — do *not* add a new skill file. + +After the dry run proves the recipe, encode it (author in native dirs, then +`node ai/scripts/generate-index.js`, then bump the plugin `version` — per `ai/CLAUDE.md`): + +1. **Extend `local-development-setup`** (`ai/skills/local-development-setup/SKILL.md`) — generic today + (zero mention of dotfiles/mac‑mini/Ollama/Hermes/MCP). Add: the dotfiles `onboard.sh` one‑liner as the + Retail‑Success Mac‑provisioning path, and a "**Local AI agent host**" section = the proven §3 + Ollama + Hermes + MCP recipe (incl. the Hermes↔Ollama wiring that actually worked) and the §6 + promotion steps. Widen the `description:` frontmatter to mention Mac provisioning + local‑model setup. +2. **Extend `developer-onboarding`** (`ai/skills/developer-onboarding/SKILL.md`) — add a pointer to the + dotfiles one‑liner and a short "your machine can run local models" note that links to (1). +3. **Optional workflow** `ai/workflows/mac-mini-agent-bootstrap.md` — the deterministic always‑on runbook. +4. **Bootstrap upgrades** (dotfiles): add a real **`--dry-run`/`DRY_RUN`** mode to + `mac-mini-agent/mac-mini-mcp-bootstrap.sh` (it has none today), and **extend it with the Ollama+Hermes + section** the `/compact` note promised — gated so the laptop run is ephemeral. +5. *(Optional)* a `project`/`reference` memory recording that hermes = Nous Hermes Agent and the + Ollama↔Hermes wiring that actually worked. + +--- + +## 6. Promote to the mac‑mini — DOCUMENTED, **not** part of the laptop dry run (O1) + +These are the *persistent* pieces. They belong on the mini only, after §3 passes: +- **Ollama as a service:** `brew services start ollama` (or a launchd plist) + pre‑pull the chosen models. +- **Hermes** default model/provider set to local, cloud **fallback** configured. +- **Always‑on rs‑agents:** the `wayroo-demo` launchd units (`RunAtLoad`, `KeepAlive`), health probe on :8765. +- **Headless survival (from `mac-mini-mcp-bootstrap.sh`):** auto‑login, Docker Desktop autostart, + `pmset`/`systemsetup` no‑sleep, Keychain auto‑unlock, MCP secrets in Keychain. +- **Remote access:** add the mini to `~/.ssh/config` (not present today); Tailscale optional (no CLI yet). + +--- + +## 7. Risks & guardrails +- **Daily driver:** `rss-mbp-5` is your work machine — the dry run must stay ephemeral (§3 golden rule). +- **Disk:** ~179 GB free — fine for a few quantized models; `ollama rm` in teardown if tight. +- **Don't touch production `onboard.sh`** until the recipe is proven; keep bootstrap changes behind `--dry-run`. +- **Secrets:** no real PATs/tokens on the laptop dry run — that's a mini‑only step. +- **PATH shadowing:** `~/.local/bin/hermes` shadows brew `hermes-agent`; pick one deliberately and pin it. +- **Hermes↔Ollama routing is unverified** — §3 Step 2 is the spike that confirms it; LM Studio is the fallback. + +--- + +## 8. Decisions (made 2026‑06‑28) +1. **Plan is the deliverable** — no changes to `rss-mbp-5` yet. Review/iterate this doc; execute §3 later. +2. **Models:** `llama3.2:3b` (smoke) + `qwen2.5-coder:7b` (coding). ✔ (already in §3 Step 1) +3. **rs‑agents capture:** extend `local-development-setup` + `developer-onboarding`; **no new skill.** ✔ (see §5) + +### When you're ready to run it +Re‑open this file and either drive §3 through `/rs-agents` (§4) or run the steps directly. Sequence: +§3 (dry run) → §5 (fold back into the two skills + add `--dry-run` to the bootstrap) → §6 (promote to mini). diff --git a/mac-mini-agent/mac-mini-mcp-bootstrap.sh b/ai/workflows/mac-mini-agent/mac-mini-mcp-bootstrap.sh similarity index 100% rename from mac-mini-agent/mac-mini-mcp-bootstrap.sh rename to ai/workflows/mac-mini-agent/mac-mini-mcp-bootstrap.sh