mcp-config: write the sync hook to ~/.zshrc.local, not ~/.zshrc

~/.zshrc is MANAGED: zsh/install.sh overwrites it wholesale (cp) on every
bootstrap run. install.sh appended the mcp-sync hook to that same file, so
the next bootstrap silently deleted it -- sync.sh then never ran, so
OPENCODE_CONFIG was never exported and opencode came up with no models and
no skills. It looked like a broken gateway or a bad model; it was a wiped
shell hook.

~/.zshrc.local is the unmanaged file the managed zshrc sources last, so the
hook now survives a re-bootstrap. Verified: hook lands in ~/.zshrc.local
(0600), managed ~/.zshrc is left untouched, and OPENCODE_CONFIG still
exports after simulating the bootstrap overwrite.

Also warn instead of failing silently when python3 is missing: sync.sh then
deploys the base config verbatim with an EMPTY models map, which presents as
"the gateway has no models" rather than "a dependency is absent".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-13 13:29:07 -04:00
co-authored by Claude Opus 4.8
parent 485e267565
commit cb89478016
2 changed files with 16 additions and 3 deletions
+10 -3
View File
@@ -53,13 +53,20 @@ chmod 0600 "$KEYFILE"
say "Key stored at $KEYFILE (0600)."
# --- 3. wire the shell rc --------------------------------------------------
# Detect the user's interactive rc; default to zsh on macOS, bash elsewhere.
# For zsh the hook goes in ~/.zshrc.local, NOT ~/.zshrc. The dotfiles repo
# MANAGES ~/.zshrc and overwrites it wholesale on every bootstrap
# (zsh/install.sh), so a hook written there survives only until the next
# bootstrap run — and then models and skills silently vanish, because sync.sh
# never runs and OPENCODE_CONFIG is never exported. ~/.zshrc.local is the
# unmanaged file the managed zshrc sources last, so the hook persists.
case "${SHELL:-}" in
*zsh) RC="$HOME/.zshrc" ;;
*zsh) RC="$HOME/.zshrc.local" ;;
*bash) RC="$HOME/.bashrc" ;;
*) [ -f "$HOME/.zshrc" ] && RC="$HOME/.zshrc" || RC="$HOME/.bashrc" ;;
*) [ -f "$HOME/.zshrc" ] && RC="$HOME/.zshrc.local" || RC="$HOME/.bashrc" ;;
esac
touch "$RC"
# It sits alongside the user's secrets; keep it non-readable by others.
[ "$RC" = "$HOME/.zshrc.local" ] && chmod 0600 "$RC" 2>/dev/null || true
if grep -qF "$HOOK_MARKER" "$RC" 2>/dev/null; then
say "Shell hook already present in $RC."
+6
View File
@@ -140,8 +140,14 @@ with os.fdopen(fd, "w") as f:
os.replace(tmp, dest)
PYEOF
elif [ -n "$_base" ]; then
# No python3: the base config is deployed verbatim, so its "models" map
# stays EMPTY and opencode shows the provider with zero models. That looks
# like a broken gateway rather than a missing dependency, so say so — this
# is the one case worth breaking the silence for.
mv "$_base" "$MCP_SYNC_DEST" 2>/dev/null || true
_base=""
printf 'mcp-sync: python3 not found — models cannot be discovered.\n' >&2
printf 'mcp-sync: install python3 (bootstrap.sh does), then open a new shell.\n' >&2
fi
[ -n "$_base" ] && rm -f "$_base"
}