UNPKG

@julianoczkowski/my-rules

Version:

Personal development rules, guidelines, and documentation for coding projects

266 lines (223 loc) 8.96 kB
import { writeFileSync, mkdirSync, readdirSync, readFileSync, statSync, } from "fs"; import { join } from "path"; import { fileURLToPath } from "url"; import { dirname } from "path"; import prompts from "prompts"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // Get the docs directory path const DOCS_DIR = join(__dirname, "docs"); function getFolderDescription(folderName) { const descriptions = { "Cursor Rules": "IDE-specific configuration and development standards for Cursor", "VS Code Rules": "IDE-specific configuration and development standards for VS Code", Templates: "Project templates and boilerplate code", Scripts: "Utility scripts and automation tools", Docs: "General documentation and guides", }; return descriptions[folderName] || "Documentation and configuration files"; } async function discoverAvailableFolders() { try { const folders = []; const items = readdirSync(DOCS_DIR); for (const item of items) { const itemPath = join(DOCS_DIR, item); const stat = statSync(itemPath); if (stat.isDirectory()) { folders.push({ name: item, path: item, description: getFolderDescription(item), }); } } return folders; } catch (error) { console.log(`⚠️ Error reading docs directory: ${error.message}`); return []; } } async function discoverFiles(selectedFolders) { const filesToDownload = []; for (const folderName of selectedFolders) { const folderPath = join(DOCS_DIR, folderName); try { const items = readdirSync(folderPath); for (const item of items) { const itemPath = join(folderPath, item); const stat = statSync(itemPath); if (stat.isFile()) { filesToDownload.push({ name: item, path: join(folderName, item), content: readFileSync(itemPath, "utf8"), }); } } } catch (error) { console.log(`⚠️ Error reading folder ${folderName}: ${error.message}`); } } return filesToDownload; } async function showFolderPicker(availableFolders) { try { console.log("\n📁 Select Documentation Folders to Download:"); console.log("=".repeat(50)); // Create choices for the checkbox prompt const choices = availableFolders.map((folder) => ({ title: folder.name, description: folder.description, value: folder.name, selected: false, // Start with none selected })); // Add special options choices.unshift( { title: "📥 Select All", description: "Download all available documentation folders", value: "select_all", selected: false, }, { title: "❌ Select None", description: "Skip download", value: "select_none", selected: false, } ); const response = await prompts({ type: "multiselect", name: "folders", message: "👉 Choose which folders to download:", choices: choices, instructions: "Use ↑↓ to navigate, space to select, enter to confirm", hint: "- Space to select. Return to submit", }); // Handle special selections if (response.folders && response.folders.includes("select_all")) { return availableFolders.map((f) => f.name); } if (response.folders && response.folders.includes("select_none")) { return []; } // Return selected folders (excluding special options) return response.folders ? response.folders.filter( (folder) => folder !== "select_all" && folder !== "select_none" ) : []; } catch (error) { console.log(`⚠️ Error with interactive picker: ${error.message}`); console.log("📋 Falling back to downloading all folders..."); return availableFolders.map((f) => f.name); } } async function downloadDocs(interactive = true, forceInteractive = false) { try { // Welcome screen with ASCII logo console.log(` ╔══════════════════════════════════════════════════════════════╗ ║ ║ ║ ███╗ ███╗██╗ ██╗ ██████╗ ██╗ ██╗██╗ ███████╗ ║ ║ ████╗ ████║╚██╗ ██╔╝ ██╔══██╗██║ ██║██║ ██╔════╝ ║ ║ ██╔████╔██║ ╚████╔╝ ██████╔╝██║ ██║██║ ███████╗ ║ ║ ██║╚██╔╝██║ ╚██╔╝ ██╔══██╗██║ ██║██║ ╚════██║ ║ ║ ██║ ╚═╝ ██║ ██║ ██║ ██║╚██████╔╝███████╗███████║ ║ ║ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚══════╝ ║ ║ ║ ║ 📚 Personal Development Rules & Guidelines ║ ║ ║ ╚══════════════════════════════════════════════════════════════╝ `); // Determine target directory let targetDir = process.cwd(); if (process.env.INIT_CWD) { targetDir = process.env.INIT_CWD; } // If we're inside node_modules, go up to the project root if (targetDir.includes("node_modules")) { targetDir = targetDir.split("node_modules")[0]; } const availableFolders = await discoverAvailableFolders(); if (availableFolders.length === 0) { console.log("⚠️ No documentation folders found"); return; } let selectedFolders = []; // Check if we're in a real interactive terminal or forced interactive mode const isInteractiveTerminal = (interactive && process.stdin.isTTY && process.stdout.isTTY) || forceInteractive; if (isInteractiveTerminal) { // Interactive mode - show picker selectedFolders = await showFolderPicker(availableFolders); if (selectedFolders.length === 0) { console.log("👋 No folders selected. Download cancelled."); return; } } else { // Non-interactive mode (like postinstall) - download all selectedFolders = availableFolders.map((f) => f.name); } const filesToDownload = await discoverFiles(selectedFolders); if (filesToDownload.length === 0) { console.log("⚠️ No files found to download in selected folders"); return; } console.log(`📋 Found ${filesToDownload.length} files to download`); let downloadedCount = 0; for (const file of filesToDownload) { const targetPath = join(targetDir, file.path); const targetDirPath = dirname(targetPath); try { // Create directory if it doesn't exist mkdirSync(targetDirPath, { recursive: true }); // Write file writeFileSync(targetPath, file.content, "utf8"); console.log(`📥 Downloading ${file.path}...`); console.log(`✓ Downloaded ${file.path}`); downloadedCount++; } catch (error) { console.log(`⚠️ Failed to download ${file.path}: ${error.message}`); } } console.log( `\n🎉 Download complete! (${downloadedCount}/${filesToDownload.length} files)` ); console.log("📚 Your rules and documentation are now available locally"); console.log(`📁 Files downloaded to: ${targetDir}`); if (downloadedCount > 0) { console.log("\n📖 Downloaded content:"); const folderSummary = {}; for (const file of filesToDownload) { const folderName = file.path.split("/")[0]; folderSummary[folderName] = (folderSummary[folderName] || 0) + 1; } for (const [folder, count] of Object.entries(folderSummary)) { console.log(` • ${folder}/ - ${count} files`); } } console.log( "\n💡 Visit https://github.com/julianoczkowski/my-rules for more info." ); } catch (error) { console.log(`❌ Error: ${error.message}`); // Don't exit with error code to avoid breaking npm install } } // Export the functions for CLI usage export { downloadDocs, discoverAvailableFolders, showFolderPicker }; // Run automatically if called directly (for postinstall) if (import.meta.url === `file://${process.argv[1]}`) { downloadDocs(false).catch(console.error); // Non-interactive for postinstall }