Files
moko-platform/scripts/cleanup-claude-dirs.sh
Jonathan Miller 8abc30835c feat: add cleanup script to remove .claude/ and .mcp.json from repos
Scans all repos via Gitea API and deletes .claude/ directories and
.mcp.json files that were accidentally committed. These are local
workspace configs and should be gitignored.

Authored-by: Moko Consulting
2026-05-10 14:47:04 -05:00

126 lines
4.3 KiB
Bash

#!/usr/bin/env bash
# cleanup-claude-dirs.sh — Remove .claude/ directories from all repos
#
# .claude/ is local workspace config (MCP settings, worktrees) and should
# never be committed. This script:
# 1. Checks if .claude/ exists in the repo via Gitea API
# 2. Deletes all files in .claude/ via API
# 3. Ensures .claude/ is in .gitignore
#
# Usage:
# cleanup-claude-dirs.sh # all repos
# cleanup-claude-dirs.sh MokoOnyx # one repo
#
set -euo pipefail
GITEA_URL="${GITEA_URL:-https://git.mokoconsulting.tech}"
GITEA_TOKEN="${GITEA_TOKEN:-$(cat ~/.gitea-token 2>/dev/null || echo "")}"
if [[ -z "$GITEA_TOKEN" ]]; then
echo "ERROR: GITEA_TOKEN not set"
exit 1
fi
FILTER="${1:-}"
CLEANED=0
SKIPPED=0
log() { echo "[$(date '+%H:%M:%S')] $*"; }
# Get all repos across orgs
get_repos() {
for ORG in MokoConsulting ClarksvilleFurs; do
PAGE=1
while true; do
REPOS=$(curl -sf -H "Authorization: token ${GITEA_TOKEN}" \
"${GITEA_URL}/api/v1/orgs/${ORG}/repos?page=${PAGE}&limit=50" 2>/dev/null)
[[ -z "$REPOS" || "$REPOS" == "[]" ]] && break
echo "$REPOS" | python3 -c "
import sys, json
for r in json.load(sys.stdin):
if not r.get('archived'):
print(f'{r[\"owner\"][\"login\"]}/{r[\"name\"]}|{r[\"default_branch\"]}')
" 2>/dev/null
COUNT=$(echo "$REPOS" | python3 -c "import sys,json; print(len(json.load(sys.stdin)))" 2>/dev/null)
[[ "$COUNT" -lt 50 ]] && break
PAGE=$((PAGE + 1))
done
done
}
while IFS='|' read -r FULL_NAME BRANCH; do
[[ -z "$FULL_NAME" ]] && continue
REPO_NAME="${FULL_NAME#*/}"
# Filter if specified
if [[ -n "$FILTER" && "$REPO_NAME" != "$FILTER" && "$FULL_NAME" != *"$FILTER"* ]]; then
continue
fi
# Check if .claude/ exists in the repo
CLAUDE_DIR=$(curl -sf -H "Authorization: token ${GITEA_TOKEN}" \
"${GITEA_URL}/api/v1/repos/${FULL_NAME}/contents/.claude?ref=${BRANCH}" 2>/dev/null)
if [[ -z "$CLAUDE_DIR" || "$CLAUDE_DIR" == "null" ]]; then
SKIPPED=$((SKIPPED + 1))
continue
fi
log "Cleaning ${FULL_NAME}..."
# Get all files in .claude/
FILES=$(echo "$CLAUDE_DIR" | python3 -c "
import sys, json
data = json.load(sys.stdin)
if isinstance(data, list):
for f in data:
if f.get('type') == 'file':
print(f'{f[\"path\"]}|{f[\"sha\"]}')
elif isinstance(data, dict) and data.get('type') == 'file':
print(f'{data[\"path\"]}|{data[\"sha\"]}')
" 2>/dev/null)
# Delete each file
while IFS='|' read -r FILE_PATH FILE_SHA; do
[[ -z "$FILE_PATH" ]] && continue
ENCODED_PATH=$(python3 -c "import urllib.parse; print(urllib.parse.quote('${FILE_PATH}', safe='/'))" 2>/dev/null)
RESULT=$(curl -sf -X DELETE \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"${GITEA_URL}/api/v1/repos/${FULL_NAME}/contents/${ENCODED_PATH}" \
-d "{\"sha\": \"${FILE_SHA}\", \"message\": \"chore: remove .claude/ from version control [skip ci]\", \"branch\": \"${BRANCH}\"}" \
-w "%{http_code}" -o /dev/null 2>&1)
if [[ "$RESULT" == "200" ]]; then
log " Deleted: ${FILE_PATH}"
else
log " FAIL: ${FILE_PATH} (HTTP ${RESULT})"
fi
done <<< "$FILES"
# Also check for .mcp.json
MCP_JSON=$(curl -sf -H "Authorization: token ${GITEA_TOKEN}" \
"${GITEA_URL}/api/v1/repos/${FULL_NAME}/contents/.mcp.json?ref=${BRANCH}" 2>/dev/null)
if [[ -n "$MCP_JSON" && "$MCP_JSON" != "null" ]]; then
MCP_SHA=$(echo "$MCP_JSON" | python3 -c "import sys,json; print(json.load(sys.stdin).get('sha',''))" 2>/dev/null)
if [[ -n "$MCP_SHA" ]]; then
RESULT=$(curl -sf -X DELETE \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"${GITEA_URL}/api/v1/repos/${FULL_NAME}/contents/.mcp.json" \
-d "{\"sha\": \"${MCP_SHA}\", \"message\": \"chore: remove .mcp.json from version control [skip ci]\", \"branch\": \"${BRANCH}\"}" \
-w "%{http_code}" -o /dev/null 2>&1)
[[ "$RESULT" == "200" ]] && log " Deleted: .mcp.json"
fi
fi
CLEANED=$((CLEANED + 1))
done < <(get_repos)
log "Done. Cleaned: ${CLEANED}, Skipped: ${SKIPPED}"