UNPKG

vibe-codex

Version:

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

434 lines (377 loc) โ€ข 10.5 kB
/** * Simplified commands for vibe-codex */ const inquirer = require("inquirer"); const chalk = require("chalk"); const fs = require("fs").promises; const path = require("path"); const { installHooks, uninstallHooks } = require("./installer"); const CONFIG_FILE = ".vibe-codex.json"; /** * Initialize vibe-codex in a project */ async function init() { console.log(chalk.blue("\n๐Ÿš€ Initializing vibe-codex...\n")); // Check if already initialized const configPath = path.join(process.cwd(), CONFIG_FILE); const exists = await fs .access(configPath) .then(() => true) .catch(() => false); if (exists) { const { overwrite } = await inquirer.prompt([ { type: "confirm", name: "overwrite", message: "vibe-codex is already initialized. Overwrite configuration?", default: false, }, ]); if (!overwrite) { console.log(chalk.yellow("Initialization cancelled.")); return; } } // Ask what to install const { features } = await inquirer.prompt([ { type: "checkbox", name: "features", message: "What would you like to install?", choices: [ { name: "Git hooks (pre-commit, commit-msg)", value: "gitHooks", checked: true, }, { name: "GitHub Actions workflow", value: "githubActions", checked: false, }, ], }, ]); // Select rules if git hooks are enabled let selectedRules = []; if (features.includes("gitHooks")) { const { rules } = await inquirer.prompt([ { type: "checkbox", name: "rules", message: "Select rules to enforce:", choices: [ { name: "๐Ÿ”’ Security checks (no secrets, API keys)", value: "security", checked: true, }, { name: "๐Ÿ“ Commit message format", value: "commit-format", checked: true, }, { name: "๐Ÿงช Test requirements", value: "testing", checked: false, }, { name: "๐Ÿ“š Documentation requirements", value: "documentation", checked: false, }, { name: "๐ŸŽจ Code style checks", value: "code-style", checked: false, }, ], validate: (answers) => { if (answers.length === 0) { return "Please select at least one rule"; } return true; }, }, ]); selectedRules = rules; } // Create configuration const config = { version: "3.0.0", gitHooks: features.includes("gitHooks"), githubActions: features.includes("githubActions"), rules: selectedRules, created: new Date().toISOString(), }; // Save configuration await fs.writeFile(configPath, JSON.stringify(config, null, 2)); console.log(chalk.green("โœ“ Configuration saved")); // Install selected features if (config.gitHooks) { await installHooks(config); } if (config.githubActions) { await installGitHubActions(config); } console.log(chalk.green("\nโœ… vibe-codex initialized successfully!")); console.log(chalk.gray("\nNext steps:")); console.log(chalk.gray(' - Run "vibe-codex config" to modify settings')); console.log(chalk.gray(" - Commit your changes to save the configuration")); } /** * Configure vibe-codex settings */ async function config() { console.log(chalk.blue("\nโš™๏ธ Configure vibe-codex\n")); // Load existing config const configPath = path.join(process.cwd(), CONFIG_FILE); const exists = await fs .access(configPath) .then(() => true) .catch(() => false); if (!exists) { console.log(chalk.yellow('No configuration found. Run "init" first.')); return; } const currentConfig = JSON.parse(await fs.readFile(configPath, "utf8")); // Show configuration menu const { action } = await inquirer.prompt([ { type: "list", name: "action", message: "What would you like to configure?", choices: [ { name: "๐Ÿ“ Modify rules", value: "rules", }, { name: "๐Ÿ”ง Toggle git hooks", value: "hooks", }, { name: "๐Ÿš€ Toggle GitHub Actions", value: "actions", }, { name: "๐Ÿ”„ Reset to defaults", value: "reset", }, { name: "โ† Back", value: "back", }, ], }, ]); switch (action) { case "rules": await configureRules(currentConfig); break; case "hooks": await toggleGitHooks(currentConfig); break; case "actions": await toggleGitHubActions(currentConfig); break; case "reset": await resetConfig(); break; case "back": return; } // Save updated config if not resetting if (action !== "reset" && action !== "back") { await fs.writeFile(configPath, JSON.stringify(currentConfig, null, 2)); console.log(chalk.green("\nโœ“ Configuration updated")); } } async function configureRules(config) { const { rules } = await inquirer.prompt([ { type: "checkbox", name: "rules", message: "Select rules to enforce:", choices: [ { name: "๐Ÿ”’ Security checks", value: "security", checked: config.rules.includes("security"), }, { name: "๐Ÿ“ Commit message format", value: "commit-format", checked: config.rules.includes("commit-format"), }, { name: "๐Ÿงช Test requirements", value: "testing", checked: config.rules.includes("testing"), }, { name: "๐Ÿ“š Documentation requirements", value: "documentation", checked: config.rules.includes("documentation"), }, { name: "๐ŸŽจ Code style checks", value: "code-style", checked: config.rules.includes("code-style"), }, ], }, ]); config.rules = rules; config.updated = new Date().toISOString(); } async function toggleGitHooks(config) { const newState = !config.gitHooks; config.gitHooks = newState; config.updated = new Date().toISOString(); if (newState) { await installHooks(config); console.log(chalk.green("โœ“ Git hooks enabled")); } else { await uninstallHooks(); console.log(chalk.yellow("โœ“ Git hooks disabled")); } } async function toggleGitHubActions(config) { const newState = !config.githubActions; config.githubActions = newState; config.updated = new Date().toISOString(); if (newState) { await installGitHubActions(config); console.log(chalk.green("โœ“ GitHub Actions enabled")); } else { await uninstallGitHubActions(); console.log(chalk.yellow("โœ“ GitHub Actions disabled")); } } async function resetConfig() { const { confirm } = await inquirer.prompt([ { type: "confirm", name: "confirm", message: "Are you sure you want to reset to default configuration?", default: false, }, ]); if (confirm) { await init(); } } /** * Uninstall vibe-codex from a project */ async function uninstall() { console.log(chalk.yellow("\n๐Ÿ—‘๏ธ Uninstall vibe-codex\n")); const { confirm } = await inquirer.prompt([ { type: "confirm", name: "confirm", message: "This will remove all vibe-codex configurations and hooks. Continue?", default: false, }, ]); if (!confirm) { console.log(chalk.gray("Uninstall cancelled.")); return; } // Remove git hooks await uninstallHooks(); // Remove GitHub Actions await uninstallGitHubActions(); // Remove config file const configPath = path.join(process.cwd(), CONFIG_FILE); try { await fs.unlink(configPath); console.log(chalk.green("โœ“ Configuration removed")); } catch (error) { // Config file might not exist } console.log(chalk.green("\nโœ… vibe-codex uninstalled successfully!")); } /** * Install GitHub Actions workflow */ async function installGitHubActions(config) { const workflowDir = path.join(process.cwd(), ".github", "workflows"); const workflowPath = path.join(workflowDir, "vibe-codex.yml"); // Create workflow directory await fs.mkdir(workflowDir, { recursive: true }); // Create workflow content based on selected rules const workflowContent = generateGitHubActionsWorkflow(config); await fs.writeFile(workflowPath, workflowContent); console.log(chalk.green("โœ“ GitHub Actions workflow installed")); } /** * Uninstall GitHub Actions workflow */ async function uninstallGitHubActions() { const workflowPath = path.join( process.cwd(), ".github", "workflows", "vibe-codex.yml", ); try { await fs.unlink(workflowPath); console.log(chalk.green("โœ“ GitHub Actions workflow removed")); } catch (error) { // Workflow might not exist } } /** * Generate GitHub Actions workflow based on configuration */ function generateGitHubActionsWorkflow(config) { const rules = config.rules || []; let steps = []; // Always checkout code steps.push(` - uses: actions/checkout@v4`); // Add steps based on rules if (rules.includes("security")) { steps.push(` - name: Security Check run: | # Check for secrets and API keys ! grep -r -E "(api_key|apikey|secret|password|token)\\s*=\\s*['\\"'][^'\\""]+['\\"']" . --include="*.js" --include="*.ts" --include="*.py" || (echo "Found potential secrets in code" && exit 1)`); } if (rules.includes("testing")) { steps.push(` - name: Run Tests run: | # Run tests based on package.json script if [ -f "package.json" ] && grep -q '"test"' package.json; then npm test fi`); } if (rules.includes("documentation")) { steps.push(` - name: Check Documentation run: | # Ensure README exists [ -f "README.md" ] || (echo "README.md is required" && exit 1)`); } return `name: vibe-codex checks on: push: branches: [ main, master, develop ] pull_request: branches: [ main, master, develop ] jobs: check: runs-on: ubuntu-latest steps: ${steps.join("\n")} `; } module.exports = { init, config, uninstall, };