diff --git a/AUTO-COMMIT-README.md b/AUTO-COMMIT-README.md new file mode 100644 index 0000000..55800a3 --- /dev/null +++ b/AUTO-COMMIT-README.md @@ -0,0 +1,174 @@ +# Auto-Commit System for Timeshift Project + +This system automatically saves your work to git to prevent losing changes when Claude gets restarted or when making experimental changes. + +## ๐Ÿš€ Quick Start + +### Start Auto-Watch (Recommended) +```bash +npm run watch-commit +``` +This will automatically commit changes every 30 seconds when files are modified. + +### Manual Commands +```bash +# Save current work immediately +npm run save "Your commit message" + +# Create a restore point before major changes +npm run restore-point "Before major refactor" + +# List all restore points +./auto-commit.sh list + +# Clean old restore points (keeps last 10) +./auto-commit.sh cleanup +``` + +## ๐Ÿ“‹ How It Works + +### Automatic Commits +- **File Watcher**: Monitors `components/`, `app/`, `lib/`, `styles/` directories +- **Smart Delay**: Waits 30 seconds after last change before committing +- **Rate Limiting**: Max 10 commits per hour to avoid spam +- **File Types**: Watches `.tsx`, `.ts`, `.js`, `.jsx`, `.css`, `.scss`, `.json` files + +### Restore Points +- **Branches**: Creates timestamped branches like `restore-point-20250129-143022` +- **Automatic**: Creates restore points on startup and every 30 minutes +- **Manual**: Create before risky changes with `npm run restore-point "description"` + +## ๐Ÿ”ง Features + +### Auto-Commit Script Features +โœ… Automatic timestamped commits +โœ… Descriptive commit messages +โœ… Rate limiting to prevent spam +โœ… Restore point creation +โœ… Cleanup of old restore points +โœ… Git status checking + +### File Watcher Features +โœ… Real-time file monitoring +โœ… Smart batching of changes +โœ… Graceful shutdown handling +โœ… Periodic restore points +โœ… Console logging for transparency + +## ๐Ÿ“š Usage Examples + +### Starting Your Work Session +```bash +# Start the auto-commit watcher +npm run watch-commit + +# In another terminal, start development +npm run dev +``` + +### Before Making Risky Changes +```bash +# Create a restore point +npm run restore-point "Before implementing new feature" + +# Make your changes... +# Files are auto-committed as you work + +# If something breaks, restore with: +git checkout restore-point-20250129-143022 +``` + +### Manual Saves +```bash +# Quick save +npm run save + +# Save with custom message +npm run save "Fixed spreadsheet column alignment" +``` + +### Managing Restore Points +```bash +# List all restore points +./auto-commit.sh list + +# Clean up old ones (keeps last 10) +./auto-commit.sh cleanup +``` + +## ๐ŸŽฏ Benefits + +1. **Never Lose Work**: Changes are saved every 30 seconds automatically +2. **Easy Recovery**: Restore points let you go back to working states +3. **Experiment Safely**: Try changes knowing you can always revert +4. **Claude Restarts**: No more losing progress when Claude session ends +5. **Version History**: Full git history of all changes with timestamps + +## โš™๏ธ Configuration + +### Modify Watch Settings +Edit `watch-and-commit.js`: +```javascript +const COMMIT_DELAY = 30000; // 30 seconds +const MAX_COMMITS_PER_HOUR = 10; +const WATCH_DIRS = ['components', 'app', 'lib', 'styles']; +``` + +### Modify Auto-Commit Behavior +Edit `auto-commit.sh` to change commit message format or add custom logic. + +## ๐Ÿ›Ÿ Recovery Scenarios + +### Claude Gets Restarted +Your work is automatically saved! Just continue from where you left off. + +### Experimental Changes Break Things +```bash +# List restore points to find a good one +./auto-commit.sh list + +# Checkout the restore point +git checkout restore-point-20250129-143022 + +# Or continue from main with your auto-commits +git checkout main +git log --oneline -10 # See recent auto-commits +``` + +### Need to Go Back a Few Commits +```bash +# See recent commits +git log --oneline -10 + +# Reset to a specific commit (keep files) +git reset --soft HEAD~3 + +# Or create a new branch from specific commit +git checkout -b fix-attempt abc1234 +``` + +## ๐Ÿšฆ Status Indicators + +The watcher shows: +- ๐Ÿ” **Started**: Auto-commit watcher is running +- ๐Ÿ“ **File changed**: A file was modified +- ๐Ÿ”„ **Performing auto-commit**: About to save changes +- โœ… **Auto-commit successful**: Changes saved +- ๐Ÿ”– **Creating restore point**: Making a backup branch +- โธ๏ธ **Rate limit reached**: Too many commits, pausing + +## ๐ŸŽฎ Quick Reference + +| Command | Purpose | +|---------|---------| +| `npm run watch-commit` | Start automatic file watching | +| `npm run save` | Immediate manual commit | +| `npm run restore-point` | Create backup branch | +| `./auto-commit.sh list` | Show restore points | +| `./auto-commit.sh cleanup` | Remove old restore points | +| `git checkout restore-point-XXXXXX` | Restore to backup | +| `git log --oneline -10` | See recent commits | + +--- + +**Pro Tip**: Always run `npm run watch-commit` when starting work with Claude to ensure nothing gets lost! ๐Ÿ›ก๏ธ \ No newline at end of file diff --git a/auto-commit.sh b/auto-commit.sh new file mode 100755 index 0000000..ebf1280 --- /dev/null +++ b/auto-commit.sh @@ -0,0 +1,106 @@ +#!/bin/bash + +# Auto-commit script for timeshift project +# Usage: ./auto-commit.sh [message] + +set -e + +# Function to auto-commit changes +auto_commit() { + local message="$1" + local timestamp=$(date '+%Y-%m-%d %H:%M:%S') + + # Check if there are any changes + if [[ -n $(git status --porcelain) ]]; then + echo "๐Ÿ”„ Auto-committing changes at $timestamp" + + # Add all changes + git add . + + # Create commit message + if [[ -z "$message" ]]; then + message="Auto-commit: Save work in progress" + fi + + # Commit with timestamp + git commit -m "$(cat < +EOF +)" + + echo "โœ… Changes committed successfully" + + # Show short log + git log --oneline -3 + else + echo "๐Ÿ“ No changes to commit at $timestamp" + fi +} + +# Function to create a safe restore point +create_restore_point() { + local description="$1" + local branch_name="restore-point-$(date '+%Y%m%d-%H%M%S')" + + echo "๐Ÿ”– Creating restore point: $branch_name" + + # Commit current changes first + auto_commit "Save before creating restore point: $description" + + # Create a new branch as restore point + git branch "$branch_name" + + echo "โœ… Restore point created: $branch_name" + echo "๐Ÿ“‹ To restore later, run: git checkout $branch_name" +} + +# Function to show available restore points +list_restore_points() { + echo "๐Ÿ“‹ Available restore points:" + git branch | grep "restore-point-" | sed 's/^..//' | sort -r +} + +# Function to clean old restore points (keep last 10) +cleanup_restore_points() { + echo "๐Ÿงน Cleaning old restore points (keeping last 10)..." + git branch | grep "restore-point-" | sed 's/^..//' | sort -r | tail -n +11 | while read branch; do + git branch -D "$branch" 2>/dev/null && echo "๐Ÿ—‘๏ธ Deleted old restore point: $branch" + done +} + +# Main script logic +case "$1" in + "commit") + auto_commit "$2" + ;; + "restore-point") + create_restore_point "$2" + ;; + "list") + list_restore_points + ;; + "cleanup") + cleanup_restore_points + ;; + *) + echo "๐Ÿ“š Auto-commit utility for timeshift project" + echo "" + echo "Usage:" + echo " $0 commit [message] - Auto-commit current changes" + echo " $0 restore-point [desc] - Create a restore point branch" + echo " $0 list - List available restore points" + echo " $0 cleanup - Clean old restore points" + echo "" + echo "Examples:" + echo " $0 commit 'Fixed spreadsheet layout'" + echo " $0 restore-point 'Before major refactor'" + echo "" + auto_commit "$1" + ;; +esac \ No newline at end of file diff --git a/package.json b/package.json index 0ad4ae9..01e194f 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,13 @@ "private": true, "scripts": { "dev": "next dev", - "build": "next build", + "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "next lint", + "auto-commit": "./auto-commit.sh", + "watch-commit": "node watch-and-commit.js", + "save": "./auto-commit.sh commit", + "restore-point": "./auto-commit.sh restore-point" }, "dependencies": { "@radix-ui/react-dialog": "^1.1.14", diff --git a/watch-and-commit.js b/watch-and-commit.js new file mode 100755 index 0000000..ca4daa2 --- /dev/null +++ b/watch-and-commit.js @@ -0,0 +1,181 @@ +#!/usr/bin/env node + +const fs = require('fs'); +const path = require('path'); +const { exec } = require('child_process'); +const { promisify } = require('util'); + +const execAsync = promisify(exec); + +// Configuration +const WATCH_DIRS = ['components', 'app', 'lib', 'styles']; +const WATCH_EXTENSIONS = ['.tsx', '.ts', '.js', '.jsx', '.css', '.scss', '.json']; +const COMMIT_DELAY = 30000; // 30 seconds delay before auto-commit +const MAX_COMMITS_PER_HOUR = 10; + +class AutoCommitWatcher { + constructor() { + this.commitQueue = new Set(); + this.commitTimeout = null; + this.commitsThisHour = 0; + this.lastCommitTime = 0; + + // Reset commit counter every hour + setInterval(() => { + this.commitsThisHour = 0; + }, 3600000); + + console.log('๐Ÿ” Auto-commit watcher started'); + console.log(`๐Ÿ“ Watching directories: ${WATCH_DIRS.join(', ')}`); + console.log(`โฑ๏ธ Commit delay: ${COMMIT_DELAY/1000}s`); + console.log(`๐Ÿšฆ Max commits per hour: ${MAX_COMMITS_PER_HOUR}`); + } + + start() { + WATCH_DIRS.forEach(dir => { + const fullPath = path.join(process.cwd(), dir); + if (fs.existsSync(fullPath)) { + this.watchDirectory(fullPath); + console.log(`๐Ÿ‘€ Watching: ${fullPath}`); + } else { + console.log(`โš ๏ธ Directory not found: ${fullPath}`); + } + }); + } + + watchDirectory(dirPath) { + try { + fs.watch(dirPath, { recursive: true }, (eventType, filename) => { + if (!filename) return; + + const filePath = path.join(dirPath, filename); + const ext = path.extname(filename); + + // Check if file extension is in our watch list + if (WATCH_EXTENSIONS.includes(ext)) { + this.handleFileChange(filePath, eventType); + } + }); + } catch (error) { + console.error(`โŒ Error watching directory ${dirPath}:`, error.message); + } + } + + handleFileChange(filePath, eventType) { + const relativePath = path.relative(process.cwd(), filePath); + console.log(`๐Ÿ“ File changed: ${relativePath} (${eventType})`); + + // Add to commit queue + this.commitQueue.add(relativePath); + + // Clear existing timeout and set new one + if (this.commitTimeout) { + clearTimeout(this.commitTimeout); + } + + this.commitTimeout = setTimeout(() => { + this.performAutoCommit(); + }, COMMIT_DELAY); + } + + async performAutoCommit() { + // Rate limiting + if (this.commitsThisHour >= MAX_COMMITS_PER_HOUR) { + console.log(`โธ๏ธ Rate limit reached (${MAX_COMMITS_PER_HOUR} commits/hour). Skipping auto-commit.`); + this.commitQueue.clear(); + return; + } + + // Check if enough time has passed since last commit + const now = Date.now(); + if (now - this.lastCommitTime < COMMIT_DELAY) { + console.log('โธ๏ธ Too soon since last commit. Skipping.'); + return; + } + + if (this.commitQueue.size === 0) { + console.log('๐Ÿ“‹ No files in commit queue'); + return; + } + + try { + // Check if there are actually changes to commit + const { stdout: status } = await execAsync('git status --porcelain'); + if (!status.trim()) { + console.log('๐Ÿ“ No actual changes to commit'); + this.commitQueue.clear(); + return; + } + + console.log('๐Ÿ”„ Performing auto-commit...'); + + // Create commit message with changed files + const changedFiles = Array.from(this.commitQueue).slice(0, 5); // Limit to 5 files in message + const fileList = changedFiles.join(', '); + const moreFiles = this.commitQueue.size > 5 ? ` and ${this.commitQueue.size - 5} more` : ''; + + const message = `Auto-save: Updated ${fileList}${moreFiles}`; + + // Use our auto-commit script + await execAsync(`./auto-commit.sh commit "${message}"`); + + console.log('โœ… Auto-commit successful'); + this.commitsThisHour++; + this.lastCommitTime = now; + this.commitQueue.clear(); + + } catch (error) { + console.error('โŒ Auto-commit failed:', error.message); + } + } + + async createRestorePoint(description = 'Auto restore point') { + try { + console.log('๐Ÿ”– Creating restore point...'); + await execAsync(`./auto-commit.sh restore-point "${description}"`); + console.log('โœ… Restore point created'); + } catch (error) { + console.error('โŒ Failed to create restore point:', error.message); + } + } +} + +// Handle process termination gracefully +process.on('SIGINT', async () => { + console.log('\n๐Ÿ›‘ Stopping auto-commit watcher...'); + + // Perform final commit if there are pending changes + try { + const { stdout: status } = await execAsync('git status --porcelain'); + if (status.trim()) { + console.log('๐Ÿ’พ Saving final changes...'); + await execAsync('./auto-commit.sh commit "Final auto-save before shutdown"'); + } + } catch (error) { + console.error('โŒ Error during shutdown save:', error.message); + } + + console.log('๐Ÿ‘‹ Auto-commit watcher stopped'); + process.exit(0); +}); + +// Start the watcher +const watcher = new AutoCommitWatcher(); +watcher.start(); + +// Create restore point on startup +setTimeout(() => { + watcher.createRestorePoint('Session start'); +}, 5000); + +// Create periodic restore points (every 30 minutes) +setInterval(() => { + watcher.createRestorePoint('Periodic backup'); +}, 30 * 60 * 1000); + +console.log('๐Ÿš€ Auto-commit system ready! Press Ctrl+C to stop.'); +console.log('๐Ÿ“‹ Commands available:'); +console.log(' ./auto-commit.sh commit [message]'); +console.log(' ./auto-commit.sh restore-point [description]'); +console.log(' ./auto-commit.sh list'); +console.log(' ./auto-commit.sh cleanup'); \ No newline at end of file