Session log: /save + /sync multi-user change summaries
Enhance /save and /sync slash commands to attribute commits by author so Mike and Howard can see at a glance what the other person did. - sync.sh: loads identity.json, shows incoming/outgoing commits with author + age before pull/push, groups by author in final summary - sync.md: describes the new output format + conflict attribution - save.md: pre-commit Change Summary block + post-commit Summary Motivation: repo is now shared across team, `git log` alone made it hard to see "when did Howard change that?" without hunting.
This commit is contained in:
@@ -1,118 +1,158 @@
|
||||
#!/bin/bash
|
||||
# ClaudeTools Bidirectional Sync Script
|
||||
# Ensures proper pull BEFORE push on all machines
|
||||
# Prints incoming/outgoing change summary with author attribution
|
||||
|
||||
set -e # Exit on error
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Detect machine name
|
||||
# Machine + timestamp
|
||||
if [ -n "$COMPUTERNAME" ]; then
|
||||
MACHINE="$COMPUTERNAME"
|
||||
else
|
||||
MACHINE=$(hostname)
|
||||
fi
|
||||
|
||||
# Timestamp
|
||||
TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
|
||||
|
||||
echo -e "${GREEN}[OK]${NC} Starting ClaudeTools sync from $MACHINE at $TIMESTAMP"
|
||||
|
||||
# Navigate to ClaudeTools directory
|
||||
if [ -d "$HOME/ClaudeTools" ]; then
|
||||
cd "$HOME/ClaudeTools"
|
||||
elif [ -d "/d/ClaudeTools" ]; then
|
||||
cd "/d/ClaudeTools"
|
||||
elif [ -d "D:/ClaudeTools" ]; then
|
||||
cd "D:/ClaudeTools"
|
||||
else
|
||||
echo -e "${RED}[ERROR]${NC} ClaudeTools directory not found"
|
||||
# Navigate to ClaudeTools directory (check common locations)
|
||||
for candidate in "$HOME/ClaudeTools" "/d/ClaudeTools" "D:/ClaudeTools" "/d/claudetools" "D:/claudetools"; do
|
||||
if [ -d "$candidate" ]; then
|
||||
cd "$candidate"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ! -d ".git" ]; then
|
||||
echo -e "${RED}[ERROR]${NC} Not in a git working tree"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}[OK]${NC} Working directory: $(pwd)"
|
||||
|
||||
# Phase 1: Check and commit local changes
|
||||
# Load user identity
|
||||
USER_DISPLAY="unknown"
|
||||
USER_GITEA=""
|
||||
if [ -f ".claude/identity.json" ]; then
|
||||
USER_DISPLAY=$(python -c "import json,sys; d=json.load(open('.claude/identity.json')); print(d.get('full_name', d.get('user','unknown')))" 2>/dev/null || echo "unknown")
|
||||
USER_GITEA=$(python -c "import json,sys; d=json.load(open('.claude/identity.json')); print(d.get('user',''))" 2>/dev/null || echo "")
|
||||
fi
|
||||
echo -e "${GREEN}[OK]${NC} Syncing as: $USER_DISPLAY (machine: $MACHINE)"
|
||||
|
||||
# Phase 1: Local changes
|
||||
echo ""
|
||||
echo "=== Phase 1: Local Changes ==="
|
||||
echo "=== Phase 1: Local changes ==="
|
||||
|
||||
if ! git diff-index --quiet HEAD -- 2>/dev/null; then
|
||||
echo -e "${YELLOW}[INFO]${NC} Local changes detected"
|
||||
|
||||
# Show status
|
||||
echo -e "${YELLOW}[INFO]${NC} Local changes detected:"
|
||||
git status --short
|
||||
echo ""
|
||||
|
||||
# Stage all changes
|
||||
echo -e "${GREEN}[OK]${NC} Staging all changes..."
|
||||
git add -A
|
||||
|
||||
# Commit with timestamp
|
||||
COMMIT_MSG="sync: Auto-sync from $MACHINE at $TIMESTAMP
|
||||
|
||||
Synced files:
|
||||
- Session logs updated
|
||||
- Latest context and credentials
|
||||
- Command/directive updates
|
||||
# Commit message (Co-Authored-By uses local git user if configured)
|
||||
COMMIT_MSG="sync: auto-sync from $MACHINE at $TIMESTAMP
|
||||
|
||||
Author: $USER_DISPLAY
|
||||
Machine: $MACHINE
|
||||
Timestamp: $TIMESTAMP
|
||||
|
||||
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>"
|
||||
Timestamp: $TIMESTAMP"
|
||||
|
||||
git commit -m "$COMMIT_MSG"
|
||||
echo -e "${GREEN}[OK]${NC} Changes committed"
|
||||
echo -e "${GREEN}[OK]${NC} Committed."
|
||||
else
|
||||
echo -e "${GREEN}[OK]${NC} No local changes to commit"
|
||||
echo -e "${GREEN}[OK]${NC} No local changes to commit."
|
||||
fi
|
||||
|
||||
# Phase 2: Sync with remote (CRITICAL: Pull BEFORE Push)
|
||||
# Phase 2: Remote sync
|
||||
echo ""
|
||||
echo "=== Phase 2: Remote Sync (Pull + Push) ==="
|
||||
echo "=== Phase 2: Fetch + inspect ==="
|
||||
|
||||
# Fetch to see what's available
|
||||
echo -e "${GREEN}[OK]${NC} Fetching from remote..."
|
||||
git fetch origin
|
||||
LOCAL_BEFORE=$(git rev-parse HEAD)
|
||||
|
||||
# Check if remote has updates
|
||||
LOCAL=$(git rev-parse main)
|
||||
REMOTE=$(git rev-parse origin/main)
|
||||
echo -e "${GREEN}[OK]${NC} Fetching from origin..."
|
||||
git fetch origin --quiet
|
||||
|
||||
if [ "$LOCAL" != "$REMOTE" ]; then
|
||||
echo -e "${YELLOW}[INFO]${NC} Remote has updates, pulling..."
|
||||
LOCAL=$(git rev-parse HEAD)
|
||||
REMOTE=$(git rev-parse origin/main 2>/dev/null || git rev-parse origin/master 2>/dev/null || echo "$LOCAL")
|
||||
REMOTE_BRANCH="origin/main"
|
||||
if ! git rev-parse origin/main >/dev/null 2>&1; then
|
||||
REMOTE_BRANCH="origin/master"
|
||||
fi
|
||||
|
||||
# Pull with rebase
|
||||
# Count and show incoming
|
||||
INCOMING_COUNT=$(git rev-list --count HEAD..$REMOTE_BRANCH 2>/dev/null || echo 0)
|
||||
OUTGOING_COUNT=$(git rev-list --count $REMOTE_BRANCH..HEAD 2>/dev/null || echo 0)
|
||||
|
||||
if [ "$INCOMING_COUNT" -gt 0 ]; then
|
||||
echo ""
|
||||
echo -e "${CYAN}--- Incoming: $INCOMING_COUNT commits from remote ---${NC}"
|
||||
git log --oneline --format=' %C(yellow)%h%Creset %C(cyan)%an%Creset %s %C(dim)(%ar)%Creset' HEAD..$REMOTE_BRANCH | head -30
|
||||
echo ""
|
||||
echo -e "${CYAN}--- Files touched by incoming commits ---${NC}"
|
||||
git diff --stat HEAD..$REMOTE_BRANCH | tail -20
|
||||
else
|
||||
echo -e "${GREEN}[OK]${NC} No incoming changes."
|
||||
fi
|
||||
|
||||
if [ "$OUTGOING_COUNT" -gt 0 ]; then
|
||||
echo ""
|
||||
echo -e "${CYAN}--- Outgoing: $OUTGOING_COUNT commits to remote ---${NC}"
|
||||
git log --oneline --format=' %C(yellow)%h%Creset %C(cyan)%an%Creset %s %C(dim)(%ar)%Creset' $REMOTE_BRANCH..HEAD | head -30
|
||||
fi
|
||||
|
||||
# Phase 3: Pull (if needed)
|
||||
if [ "$INCOMING_COUNT" -gt 0 ]; then
|
||||
echo ""
|
||||
echo "=== Phase 3: Pull (rebase) ==="
|
||||
if git pull origin main --rebase; then
|
||||
echo -e "${GREEN}[OK]${NC} Successfully pulled remote changes"
|
||||
git log --oneline "$LOCAL..origin/main"
|
||||
echo -e "${GREEN}[OK]${NC} Pulled successfully."
|
||||
else
|
||||
echo -e "${RED}[ERROR]${NC} Pull failed - may have conflicts"
|
||||
echo -e "${YELLOW}[INFO]${NC} Resolve conflicts and run sync again"
|
||||
echo -e "${RED}[ERROR]${NC} Pull failed (likely conflicts). Resolve and re-run sync."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Phase 4: Push (if needed)
|
||||
OUTGOING_AFTER_PULL=$(git rev-list --count $REMOTE_BRANCH..HEAD 2>/dev/null || echo 0)
|
||||
if [ "$OUTGOING_AFTER_PULL" -gt 0 ]; then
|
||||
echo ""
|
||||
echo "=== Phase 4: Push ==="
|
||||
if git push origin main; then
|
||||
echo -e "${GREEN}[OK]${NC} Pushed successfully."
|
||||
else
|
||||
echo -e "${RED}[ERROR]${NC} Push failed. Check auth / network."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo -e "${GREEN}[OK]${NC} Already up to date with remote"
|
||||
echo -e "${GREEN}[OK]${NC} Nothing to push."
|
||||
fi
|
||||
|
||||
# Push local changes
|
||||
# Phase 5: Summary
|
||||
echo ""
|
||||
echo -e "${GREEN}[OK]${NC} Pushing local changes to remote..."
|
||||
if git push origin main; then
|
||||
echo -e "${GREEN}[OK]${NC} Successfully pushed to remote"
|
||||
else
|
||||
echo -e "${RED}[ERROR]${NC} Push failed"
|
||||
exit 1
|
||||
echo "=== Sync Summary ==="
|
||||
|
||||
if [ "$INCOMING_COUNT" -gt 0 ]; then
|
||||
# Count commits by author
|
||||
INCOMING_AUTHORS=$(git log --format='%an' $LOCAL_BEFORE..HEAD 2>/dev/null | sort | uniq -c | sort -rn | awk '{printf "%s (%s), ", substr($0, index($0,$2)), $1}' | sed 's/, $//')
|
||||
echo -e "${CYAN}Pulled in:${NC} $INCOMING_COUNT commit(s) — authors: ${INCOMING_AUTHORS:-unknown}"
|
||||
fi
|
||||
if [ "$OUTGOING_AFTER_PULL" -gt 0 ]; then
|
||||
echo -e "${CYAN}Pushed out:${NC} $OUTGOING_AFTER_PULL commit(s) by $USER_DISPLAY"
|
||||
fi
|
||||
if [ "$INCOMING_COUNT" -eq 0 ] && [ "$OUTGOING_AFTER_PULL" -eq 0 ]; then
|
||||
echo -e "${GREEN}Already in sync — no commits moved in either direction.${NC}"
|
||||
fi
|
||||
|
||||
# Phase 3: Report final status
|
||||
echo ""
|
||||
echo "=== Sync Complete ==="
|
||||
echo -e "${GREEN}[OK]${NC} Local branch: $(git rev-parse --abbrev-ref HEAD)"
|
||||
echo -e "${GREEN}[OK]${NC} Current commit: $(git log -1 --oneline)"
|
||||
echo -e "${GREEN}[OK]${NC} Remote status: $(git status -sb | head -1)"
|
||||
echo -e "${GREEN}[OK]${NC} HEAD: $(git log -1 --oneline)"
|
||||
echo -e "${GREEN}[OK]${NC} Status: $(git status -sb | head -1)"
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}[SUCCESS]${NC} All machines in sync. Ready to continue work."
|
||||
echo -e "${GREEN}[SUCCESS]${NC} Sync complete."
|
||||
|
||||
Reference in New Issue
Block a user