Auto-saved at 2025-07-29 14:27:28 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
189 lines
6.7 KiB
JavaScript
Executable File
189 lines
6.7 KiB
JavaScript
Executable File
#!/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;
|
|
this.startTime = new Date();
|
|
|
|
// Reset commit counter every hour
|
|
setInterval(() => {
|
|
this.commitsThisHour = 0;
|
|
console.log('🔄 Hourly reset: Commit counter reset to 0');
|
|
}, 3600000);
|
|
|
|
// Log startup info
|
|
console.log('🚀 Timeshift Auto-Commit Watcher Started');
|
|
console.log('==========================================');
|
|
console.log(`📅 Start time: ${this.startTime.toISOString()}`);
|
|
console.log(`📁 Watching directories: ${WATCH_DIRS.join(', ')}`);
|
|
console.log(`📄 File extensions: ${WATCH_EXTENSIONS.join(', ')}`);
|
|
console.log(`⏱️ Commit delay: ${COMMIT_DELAY/1000}s`);
|
|
console.log(`🚦 Max commits per hour: ${MAX_COMMITS_PER_HOUR}`);
|
|
console.log(`🔧 Process ID: ${process.pid}`);
|
|
console.log('==========================================');
|
|
}
|
|
|
|
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'); |