Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 31 additions & 45 deletions .claude/session-start-global-deny.sh
Original file line number Diff line number Diff line change
@@ -1,43 +1,29 @@
#!/bin/bash
# CANONICAL SOURCE — managed by runcycles/.github/shared-config/
# Do not edit this file in individual repos. Changes should be made here
# and synced to all repos via scripts/sync-claude-config.sh.
# Session start hook: ensure global Claude Code deny rules and git proxy config
#
# Session start hook: ensure global Claude Code deny rules and git proxy config.
#
# This script runs at session start and does TWO things:
#
# 1. (Per-user, idempotent) Writes MCP deny rules to ~/.claude/settings.json
# so mcp__github__ file-mutation tools are blocked globally — even in
# cross-repo sessions where deny rules in a single repo wouldn't apply.
#
# 2. (MULTI-REPO MUTATION) If a local git proxy is detected in any sibling
# repo under /home/user/*, rewrites the `origin` remote URL of EVERY
# sibling github.com repo under /home/user/ to route through the proxy.
# This is intentional for Claude Code remote-environment workflows where
# multiple Cycles repos are cloned side-by-side and all need the same
# proxy. It is surprising if you only know about the per-repo .claude/
# hook, so it is called out explicitly here.
#
# OPT-OUT: set CYCLES_CLAUDE_SKIP_REMOTE_REWRITE=1 to disable Part 2 entirely
# (useful for local Claude Code runs where you do not want sibling repos
# touched). Part 1 always runs.
#
# Issue: runcycles/.github#63
# 1. Writes MCP deny rules to ~/.claude/settings.json so mcp__github__
# file-mutation tools are blocked globally (even in cross-repo sessions).
# 2. Fixes git remote URLs to use the local git proxy when available,
# so native git push works instead of falling back to MCP tools.

set -e

# --- Part 1: Global MCP deny rules ---

GLOBAL_SETTINGS="$HOME/.claude/settings.json"

if ! [ -f "$GLOBAL_SETTINGS" ] || ! grep -q "mcp__github__push_files" "$GLOBAL_SETTINGS" 2>/dev/null; then
mkdir -p "$HOME/.claude"
# The previous version of this block only ran the merge when push_files was
# missing, which silently left the policy incomplete if push_files happened to
# exist while one of the other two rules had been removed. The python3 merge
# is idempotent (skips rules already present), so we now always run it on
# session start to guarantee all three deny rules are in place.
# Tracked org-wide at runcycles/.github#63.
mkdir -p "$HOME/.claude"

if [ -f "$GLOBAL_SETTINGS" ]; then
TMP_SETTINGS=$(mktemp)
if command -v python3 &>/dev/null; then
python3 -c "
if [ -f "$GLOBAL_SETTINGS" ]; then
TMP_SETTINGS=$(mktemp)
if command -v python3 &>/dev/null; then
python3 -c "
import json
with open('$GLOBAL_SETTINGS') as f:
settings = json.load(f)
Expand All @@ -56,11 +42,11 @@ with open('$TMP_SETTINGS', 'w') as f:
json.dump(settings, f, indent=2)
f.write('\n')
" && mv "$TMP_SETTINGS" "$GLOBAL_SETTINGS"
else
rm -f "$TMP_SETTINGS"
fi
else
cat > "$GLOBAL_SETTINGS" << 'EOF'
rm -f "$TMP_SETTINGS"
fi
else
cat > "$GLOBAL_SETTINGS" << 'EOF'
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
Expand All @@ -72,22 +58,22 @@ with open('$TMP_SETTINGS', 'w') as f:
}
}
EOF
fi
fi

# --- Part 2: Fix git remote URLs to use local proxy (MULTI-REPO) ---
# Some sessions clone repos via github.com directly, which lacks push credentials.
# If the local git proxy is running, rewrite remote URLs to use it.
#
# WARNING: this part iterates every directory under /home/user/ and mutates the
# `origin` remote of any github.com repo it finds, not just the current
# checkout. See the file-level header for the rationale and opt-out.

if [ "${CYCLES_CLAUDE_SKIP_REMOTE_REWRITE:-}" = "1" ]; then
# Opt-out path for local Claude Code runs that should not touch sibling repos.
# --- Part 2: Fix git remote URLs to use local proxy ---
# NOTE: This block intentionally rewrites the `origin` remote on EVERY sibling
# repo under /home/user/* with a github.com remote, not just this one. Claude
# Code remote sessions clone multiple repos and all need the local git proxy.
# To opt out (e.g., when running outside that environment, or when you want
# unrelated checkouts left alone), set CYCLES_CLAUDE_SKIP_REMOTE_REWRITE=1.
# Tracked org-wide at runcycles/.github#63.
if [ -n "$CYCLES_CLAUDE_SKIP_REMOTE_REWRITE" ]; then
exit 0
fi

# Some sessions clone repos via github.com directly, which lacks push credentials.
# If the local git proxy is running, rewrite remote URLs to use it.

# Detect local git proxy: look for the proxy in any sibling repo's remote URL
PROXY_BASE=""
for dir in /home/user/*/; do
Expand Down