ailock
Version:
AI-Proof File Guard - Protect sensitive files from accidental AI modifications
106 lines • 5.04 kB
JavaScript
import { Command } from 'commander';
import chalk from 'chalk';
import path from 'path';
import { loadConfig, findProtectedFiles } from '../core/config.js';
import { getPlatformAdapter } from '../core/platform.js';
import { hasStagedChanges } from '../core/git.js';
export const preCommitCheckCommand = new Command('pre-commit-check')
.description('Check if any staged files are locked (used by Git hooks)')
.argument('[files...]', 'Files to check (if not provided, checks all staged files)')
.option('--staged-only', 'Only check files that are staged for commit')
.action(async (files, options) => {
try {
const config = await loadConfig();
const protectedFiles = await findProtectedFiles(config);
if (protectedFiles.length === 0) {
// No protected files configured, allow commit
process.exit(0);
}
const adapter = getPlatformAdapter();
const currentDir = process.cwd();
// Determine which files to check
let filesToCheck;
if (files && files.length > 0) {
// Use provided files (from Git hook)
filesToCheck = files.map(f => path.resolve(currentDir, f));
}
else if (options.stagedOnly) {
// Check only staged files
filesToCheck = await hasStagedChanges(protectedFiles);
}
else {
// Check all protected files
filesToCheck = protectedFiles;
}
// Check which protected files are being modified and are locked
const lockedFilesBeingModified = [];
const problemFiles = [];
for (const file of filesToCheck) {
// Check if this file is in our protected list
const isProtected = protectedFiles.some(pf => path.resolve(pf) === path.resolve(file));
if (isProtected) {
try {
const isLocked = await adapter.isLocked(file);
if (isLocked) {
const relativePath = path.relative(currentDir, file);
lockedFilesBeingModified.push(file);
problemFiles.push({
file,
relativePath,
reason: 'File is locked'
});
}
}
catch (error) {
// If we can't check lock status, assume it's a problem
const relativePath = path.relative(currentDir, file);
problemFiles.push({
file,
relativePath,
reason: `Cannot check lock status: ${error instanceof Error ? error.message : String(error)}`
});
}
}
}
if (problemFiles.length === 0) {
// All clear - no locked files being modified
if (process.env.AILOCK_VERBOSE) {
console.log(chalk.green('✅ No locked files being modified'));
}
process.exit(0);
}
// We have locked files being modified - block the commit
console.error(chalk.red.bold('🔒 Commit blocked: Locked files would be modified\n'));
console.error(chalk.yellow('The following protected files are locked and cannot be committed:'));
for (const problem of problemFiles) {
console.error(chalk.red(` ❌ ${problem.relativePath}`));
if (problem.reason !== 'File is locked') {
console.error(chalk.gray(` ${problem.reason}`));
}
}
console.error('\n' + chalk.blue.bold('💡 To resolve this:'));
console.error(chalk.gray(' 1. Unlock the files you want to modify:'));
for (const problem of problemFiles) {
console.error(chalk.gray(` ailock unlock "${problem.relativePath}"`));
}
console.error(chalk.gray(' 2. Make your changes'));
console.error(chalk.gray(' 3. Lock the files again:'));
for (const problem of problemFiles) {
console.error(chalk.gray(` ailock lock "${problem.relativePath}"`));
}
console.error(chalk.gray(' 4. Commit your changes'));
console.error('\n' + chalk.blue('Alternative:'));
console.error(chalk.gray(' • Unlock all protected files: ailock unlock'));
console.error(chalk.gray(' • Make changes and commit'));
console.error(chalk.gray(' • Lock all files again: ailock lock'));
// Exit with error code to block the commit
process.exit(1);
}
catch (error) {
console.error(chalk.red('Error during pre-commit check:'), error instanceof Error ? error.message : String(error));
// On error, allow commit to proceed (fail-open for availability)
console.error(chalk.yellow('⚠️ Allowing commit to proceed due to check error'));
process.exit(0);
}
});
//# sourceMappingURL=pre-commit-check.js.map