UNPKG

vibe-codex

Version:

CLI tool to install development rules and git hooks with interactive configuration

219 lines (189 loc) 5.84 kB
/** * Simple installer for git hooks */ const fs = require("fs").promises; const path = require("path"); const chalk = require("chalk"); const HOOK_NAMES = ["pre-commit", "commit-msg"]; /** * Install git hooks based on configuration */ async function installHooks(config) { const gitDir = path.join(process.cwd(), ".git"); const hooksDir = path.join(gitDir, "hooks"); // Check if .git directory exists try { await fs.access(gitDir); } catch (error) { console.log( chalk.yellow("⚠️ Not a git repository. Skipping hooks installation."), ); return; } // Create hooks directory if it doesn't exist await fs.mkdir(hooksDir, { recursive: true }); // Install each hook for (const hookName of HOOK_NAMES) { const hookContent = generateHookContent(hookName, config); const hookPath = path.join(hooksDir, hookName); // Check if hook already exists const exists = await fs .access(hookPath) .then(() => true) .catch(() => false); if (exists) { // Backup existing hook const backupPath = `${hookPath}.vibe-codex-backup`; await fs.copyFile(hookPath, backupPath); console.log( chalk.gray( ` Backed up existing ${hookName} to ${hookName}.vibe-codex-backup`, ), ); } // Write hook content await fs.writeFile(hookPath, hookContent, { mode: 0o755 }); console.log(chalk.green(` ✓ Installed ${hookName} hook`)); } } /** * Uninstall git hooks */ async function uninstallHooks() { const hooksDir = path.join(process.cwd(), ".git", "hooks"); try { for (const hookName of HOOK_NAMES) { const hookPath = path.join(hooksDir, hookName); const backupPath = `${hookPath}.vibe-codex-backup`; // Check if our hook exists const hookExists = await fs .access(hookPath) .then(() => true) .catch(() => false); if (hookExists) { const content = await fs.readFile(hookPath, "utf8"); if (content.includes("vibe-codex")) { // Restore backup if it exists const backupExists = await fs .access(backupPath) .then(() => true) .catch(() => false); if (backupExists) { await fs.copyFile(backupPath, hookPath); await fs.unlink(backupPath); console.log(chalk.green(` ✓ Restored original ${hookName} hook`)); } else { await fs.unlink(hookPath); console.log(chalk.green(` ✓ Removed ${hookName} hook`)); } } } } } catch (error) { // Hooks directory might not exist } } /** * Generate hook content based on hook type and configuration */ function generateHookContent(hookName, config) { const rules = config.rules || []; if (hookName === "pre-commit") { return generatePreCommitHook(rules); } else if (hookName === "commit-msg") { return generateCommitMsgHook(rules); } return ""; } /** * Generate pre-commit hook content */ function generatePreCommitHook(rules) { let checks = []; if (rules.includes("security")) { checks.push(` # Security check - look for potential secrets echo "🔒 Running security checks..." if git diff --cached --name-only | xargs grep -E "(api_key|apikey|secret|password|token)\\s*=\\s*['\\"'][^'\\""]+['\\"']" 2>/dev/null; then echo "Error: Potential secrets detected in staged files!" echo "Please remove sensitive information before committing." exit 1 fi`); } if (rules.includes("testing")) { checks.push(` # Test check - run tests if available echo "🧪 Running tests..." if [ -f "package.json" ] && grep -q '"test"' package.json; then npm test || (echo "❌ Tests failed! Fix tests before committing." && exit 1) fi`); } if (rules.includes("documentation")) { checks.push(` # Documentation check echo "📚 Checking documentation..." if [ ! -f "README.md" ]; then echo "⚠️ Warning: No README.md file found" fi`); } if (rules.includes("code-style")) { checks.push(` # Code style check echo "🎨 Checking code style..." if [ -f "package.json" ] && grep -q '"lint"' package.json; then npm run lint || (echo "⚠️ Warning: Linting issues found" && exit 0) fi`); } return `#!/bin/sh # vibe-codex pre-commit hook # Generated by vibe-codex - do not edit manually echo "🚀 Running vibe-codex pre-commit checks..." ${checks.join("\n")} echo "✅ All pre-commit checks passed!" exit 0`; } /** * Generate commit-msg hook content */ function generateCommitMsgHook(rules) { if (!rules.includes("commit-format")) { return `#!/bin/sh # vibe-codex commit-msg hook # No commit message validation configured exit 0`; } return `#!/bin/sh # vibe-codex commit-msg hook # Validates commit message format commit_regex='^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\\(.+\\))?: .{1,100}$' commit_msg=$(cat "$1") echo "📝 Validating commit message format..." if ! echo "$commit_msg" | grep -qE "$commit_regex"; then echo "❌ Invalid commit message format!" echo "" echo "Expected format: type(scope): description" echo "Example: feat(auth): add login functionality" echo "" echo "Valid types:" echo " feat - New feature" echo " fix - Bug fix" echo " docs - Documentation changes" echo " style - Code style changes" echo " refactor - Code refactoring" echo " test - Test changes" echo " chore - Build/tool changes" echo " perf - Performance improvements" echo " ci - CI/CD changes" echo " build - Build system changes" echo " revert - Revert previous commit" echo "" echo "Your message: $commit_msg" exit 1 fi echo "✅ Commit message format is valid!" exit 0`; } module.exports = { installHooks, uninstallHooks, };