UNPKG

ailock

Version:

AI-Proof File Guard - Protect sensitive files from accidental AI modifications

169 lines • 7.12 kB
import { Command } from 'commander'; import chalk from 'chalk'; import { FileOperationService } from '../services/FileOperationService.js'; import { initializeUserConfig } from '../core/directory-tracker.js'; import { HooksService } from '../services/HooksService.js'; import { isGitRepository, installPreCommitHook, getRepoRoot } from '../core/git.js'; export function createLockCommand() { return new Command('lock') .description('Lock files to prevent accidental AI modifications (includes complete protection setup)') .argument('[patterns...]', 'Files or patterns to lock (uses .ailock if not specified)') .option('-a, --all', 'Lock all files defined in .ailock configuration') .option('-v, --verbose', 'Show detailed output') .option('--dry-run', 'Preview what would be locked without making changes') .option('--no-gitignore', 'Include files that are gitignored') .option('--no-hooks', 'Skip automatic hook installation') .option('--hooks-only', 'Only install hooks, skip file locking') .action(lockFileAction); } export function createProtectCommand() { return new Command('protect') .description('Complete protection setup - lock files and install all hooks') .argument('[patterns...]', 'Files or patterns to protect (uses .ailock if not specified)') .option('-a, --all', 'Protect all files defined in .ailock configuration') .option('-v, --verbose', 'Show detailed output') .option('--dry-run', 'Preview what would be protected without making changes') .option('--no-gitignore', 'Include files that are gitignored') .action(async (patterns, options) => { // Force enable hooks for protect command const protectOptions = { ...options, noHooks: false }; await lockFileAction(patterns, protectOptions); }); } async function lockFileAction(patterns, options) { try { // Initialize user configuration if needed await initializeUserConfig(); const service = new FileOperationService(); const hooksService = new HooksService(); // If hooks-only mode, just install hooks if (options.hooksOnly) { await installCompleteProtection(hooksService, options); return; } // Lock files first if (!options.dryRun) { console.log(chalk.blue.bold('šŸ”’ Step 1: Locking files...\n')); } const result = await service.processFiles('lock', { patterns, all: options.all, dryRun: options.dryRun, verbose: options.verbose, includeGitignored: !options.gitignore }); await service.displaySummary('lock', result, { dryRun: options.dryRun, verbose: options.verbose }); // Install hooks automatically unless disabled or dry-run if (!options.noHooks && !options.dryRun && result.successful.length > 0) { console.log(chalk.blue.bold('\nšŸ›”ļø Step 2: Installing AI protection...\n')); await installCompleteProtection(hooksService, options); } // Exit with error if any operations failed if (result.failed.length > 0 && !options.dryRun) { process.exit(1); } // Show complete protection status if (!options.dryRun && result.successful.length > 0) { await showProtectionStatus(hooksService); } } catch (error) { console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error)); process.exit(1); } } /** * Install complete multi-level protection */ async function installCompleteProtection(hooksService, options) { const installations = []; // 1. Install Git hooks if in Git repository try { const isRepo = await isGitRepository(); if (isRepo) { const repoRoot = await getRepoRoot(); if (repoRoot) { await installPreCommitHook(repoRoot, false); installations.push('Git pre-commit hooks'); if (options.verbose) { console.log(chalk.green(' āœ… Git hooks installed')); } } } else if (options.verbose) { console.log(chalk.gray(' ā„¹ļø Not a Git repository - skipping Git hooks')); } } catch (error) { if (options.verbose) { console.log(chalk.yellow(' āš ļø Git hooks installation failed:', error instanceof Error ? error.message : String(error))); } } // 2. Install Claude Code hooks try { const claudeInfo = hooksService.detectClaudeCode(); if (claudeInfo.detected) { const status = await hooksService.getHookStatus('claude'); if (!status.installed) { await hooksService.installClaudeHooks(claudeInfo); installations.push('Claude Code hooks'); if (options.verbose) { console.log(chalk.green(' āœ… Claude Code hooks installed')); } } else if (options.verbose) { console.log(chalk.gray(' ā„¹ļø Claude Code hooks already installed')); } } else if (options.verbose) { console.log(chalk.gray(' ā„¹ļø Claude Code not detected - skipping Claude hooks')); } } catch (error) { if (options.verbose) { console.log(chalk.yellow(' āš ļø Claude hooks installation failed:', error instanceof Error ? error.message : String(error))); } } // Summary if (installations.length > 0) { console.log(chalk.green(`āœ… Protection installed: ${installations.join(', ')}`)); } else { console.log(chalk.yellow('āš ļø No additional protection could be installed')); } } /** * Show comprehensive protection status */ async function showProtectionStatus(hooksService) { console.log(chalk.blue.bold('\nšŸ›”ļø Protection Status Summary:')); console.log(chalk.gray('─'.repeat(40))); // Check Git protection const isRepo = await isGitRepository(); if (isRepo) { console.log(chalk.green('āœ… Git Repository: Protected')); } else { console.log(chalk.gray('ā—‹ Git Repository: Not applicable')); } // Check Claude Code protection const claudeInfo = hooksService.detectClaudeCode(); if (claudeInfo.detected) { const status = await hooksService.getHookStatus('claude'); if (status.installed) { const scope = claudeInfo.isProjectLevel ? 'Project-level' : 'User-level'; console.log(chalk.green(`āœ… Claude Code: Protected (${scope})`)); } else { console.log(chalk.yellow('āš ļø Claude Code: Not protected')); } } else { console.log(chalk.gray('ā—‹ Claude Code: Not detected')); } console.log(chalk.blue('\nšŸŽ‰ Your files are now protected from accidental AI modifications!')); } //# sourceMappingURL=lock.js.map