Initial commit
This commit is contained in:
259
skills/inquirer-patterns/templates/nodejs/autocomplete-prompt.js
Normal file
259
skills/inquirer-patterns/templates/nodejs/autocomplete-prompt.js
Normal file
@@ -0,0 +1,259 @@
|
||||
/**
|
||||
* Autocomplete Prompt Template
|
||||
*
|
||||
* Use for: Large option lists with search
|
||||
* Features: Type-ahead, fuzzy matching, suggestions
|
||||
*
|
||||
* Note: Requires inquirer-autocomplete-prompt plugin
|
||||
* Install: npm install inquirer-autocomplete-prompt
|
||||
*/
|
||||
|
||||
import inquirer from 'inquirer';
|
||||
import inquirerAutocomplete from 'inquirer-autocomplete-prompt';
|
||||
|
||||
// Register the autocomplete prompt type
|
||||
inquirer.registerPrompt('autocomplete', inquirerAutocomplete);
|
||||
|
||||
// Example: Countries list for autocomplete
|
||||
const countries = [
|
||||
'Afghanistan', 'Albania', 'Algeria', 'Andorra', 'Angola',
|
||||
'Argentina', 'Armenia', 'Australia', 'Austria', 'Azerbaijan',
|
||||
'Bahamas', 'Bahrain', 'Bangladesh', 'Barbados', 'Belarus',
|
||||
'Belgium', 'Belize', 'Benin', 'Bhutan', 'Bolivia',
|
||||
'Brazil', 'Brunei', 'Bulgaria', 'Burkina Faso', 'Burundi',
|
||||
'Cambodia', 'Cameroon', 'Canada', 'Cape Verde', 'Chad',
|
||||
'Chile', 'China', 'Colombia', 'Comoros', 'Congo',
|
||||
'Costa Rica', 'Croatia', 'Cuba', 'Cyprus', 'Czech Republic',
|
||||
'Denmark', 'Djibouti', 'Dominica', 'Dominican Republic',
|
||||
'Ecuador', 'Egypt', 'El Salvador', 'Estonia', 'Ethiopia',
|
||||
'Fiji', 'Finland', 'France', 'Gabon', 'Gambia',
|
||||
'Georgia', 'Germany', 'Ghana', 'Greece', 'Grenada',
|
||||
'Guatemala', 'Guinea', 'Guyana', 'Haiti', 'Honduras',
|
||||
'Hungary', 'Iceland', 'India', 'Indonesia', 'Iran',
|
||||
'Iraq', 'Ireland', 'Israel', 'Italy', 'Jamaica',
|
||||
'Japan', 'Jordan', 'Kazakhstan', 'Kenya', 'Kuwait',
|
||||
'Laos', 'Latvia', 'Lebanon', 'Lesotho', 'Liberia',
|
||||
'Libya', 'Lithuania', 'Luxembourg', 'Madagascar', 'Malawi',
|
||||
'Malaysia', 'Maldives', 'Mali', 'Malta', 'Mexico',
|
||||
'Moldova', 'Monaco', 'Mongolia', 'Morocco', 'Mozambique',
|
||||
'Myanmar', 'Namibia', 'Nepal', 'Netherlands', 'New Zealand',
|
||||
'Nicaragua', 'Niger', 'Nigeria', 'Norway', 'Oman',
|
||||
'Pakistan', 'Panama', 'Paraguay', 'Peru', 'Philippines',
|
||||
'Poland', 'Portugal', 'Qatar', 'Romania', 'Russia',
|
||||
'Rwanda', 'Saudi Arabia', 'Senegal', 'Serbia', 'Singapore',
|
||||
'Slovakia', 'Slovenia', 'Somalia', 'South Africa', 'South Korea',
|
||||
'Spain', 'Sri Lanka', 'Sudan', 'Sweden', 'Switzerland',
|
||||
'Syria', 'Taiwan', 'Tanzania', 'Thailand', 'Togo',
|
||||
'Tunisia', 'Turkey', 'Uganda', 'Ukraine', 'United Arab Emirates',
|
||||
'United Kingdom', 'United States', 'Uruguay', 'Uzbekistan',
|
||||
'Venezuela', 'Vietnam', 'Yemen', 'Zambia', 'Zimbabwe'
|
||||
];
|
||||
|
||||
// Example: NPM packages for autocomplete
|
||||
const popularPackages = [
|
||||
'express', 'react', 'vue', 'angular', 'next', 'nuxt',
|
||||
'axios', 'lodash', 'moment', 'dayjs', 'uuid', 'dotenv',
|
||||
'typescript', 'eslint', 'prettier', 'jest', 'mocha', 'chai',
|
||||
'webpack', 'vite', 'rollup', 'babel', 'esbuild',
|
||||
'socket.io', 'redis', 'mongodb', 'mongoose', 'sequelize',
|
||||
'prisma', 'typeorm', 'knex', 'pg', 'mysql2',
|
||||
'bcrypt', 'jsonwebtoken', 'passport', 'helmet', 'cors',
|
||||
'multer', 'sharp', 'puppeteer', 'playwright', 'cheerio'
|
||||
];
|
||||
|
||||
// Fuzzy search function
|
||||
function fuzzySearch(input, choices) {
|
||||
if (!input) return choices;
|
||||
|
||||
const searchTerm = input.toLowerCase();
|
||||
return choices.filter(choice => {
|
||||
const item = typeof choice === 'string' ? choice : choice.name;
|
||||
return item.toLowerCase().includes(searchTerm);
|
||||
});
|
||||
}
|
||||
|
||||
async function autocompletePromptExample() {
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'autocomplete',
|
||||
name: 'country',
|
||||
message: 'Select your country:',
|
||||
source: (answersSoFar, input) => {
|
||||
return Promise.resolve(fuzzySearch(input, countries));
|
||||
},
|
||||
pageSize: 10
|
||||
},
|
||||
{
|
||||
type: 'autocomplete',
|
||||
name: 'package',
|
||||
message: 'Search for an npm package:',
|
||||
source: (answersSoFar, input) => {
|
||||
const filtered = fuzzySearch(input, popularPackages);
|
||||
return Promise.resolve(filtered);
|
||||
},
|
||||
pageSize: 8,
|
||||
validate: (input) => {
|
||||
return input.length > 0 || 'Please select a package';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'autocomplete',
|
||||
name: 'city',
|
||||
message: 'Select city:',
|
||||
source: async (answersSoFar, input) => {
|
||||
// Example: Cities based on selected country
|
||||
const citiesByCountry = {
|
||||
'United States': ['New York', 'Los Angeles', 'Chicago', 'Houston', 'Phoenix'],
|
||||
'United Kingdom': ['London', 'Manchester', 'Birmingham', 'Glasgow', 'Liverpool'],
|
||||
'Canada': ['Toronto', 'Vancouver', 'Montreal', 'Calgary', 'Ottawa'],
|
||||
'Australia': ['Sydney', 'Melbourne', 'Brisbane', 'Perth', 'Adelaide'],
|
||||
'Germany': ['Berlin', 'Munich', 'Hamburg', 'Frankfurt', 'Cologne']
|
||||
};
|
||||
|
||||
const cities = citiesByCountry[answersSoFar.country] || ['Capital City', 'Major City'];
|
||||
return fuzzySearch(input, cities);
|
||||
},
|
||||
when: (answers) => ['United States', 'United Kingdom', 'Canada', 'Australia', 'Germany'].includes(answers.country)
|
||||
}
|
||||
]);
|
||||
|
||||
console.log('\n✅ Selections:');
|
||||
console.log(JSON.stringify(answers, null, 2));
|
||||
|
||||
return answers;
|
||||
}
|
||||
|
||||
// Example: Framework/Library search
|
||||
async function frameworkSearchExample() {
|
||||
const frameworks = [
|
||||
{ name: 'React - UI library by Facebook', value: 'react' },
|
||||
{ name: 'Vue.js - Progressive JavaScript framework', value: 'vue' },
|
||||
{ name: 'Angular - Platform for building web apps', value: 'angular' },
|
||||
{ name: 'Svelte - Cybernetically enhanced web apps', value: 'svelte' },
|
||||
{ name: 'Next.js - React framework with SSR', value: 'next' },
|
||||
{ name: 'Nuxt.js - Vue.js framework with SSR', value: 'nuxt' },
|
||||
{ name: 'Remix - Full stack web framework', value: 'remix' },
|
||||
{ name: 'SvelteKit - Svelte framework', value: 'sveltekit' },
|
||||
{ name: 'Express - Fast Node.js web framework', value: 'express' },
|
||||
{ name: 'Fastify - Fast and low overhead web framework', value: 'fastify' },
|
||||
{ name: 'NestJS - Progressive Node.js framework', value: 'nestjs' },
|
||||
{ name: 'Koa - Expressive middleware for Node.js', value: 'koa' }
|
||||
];
|
||||
|
||||
const answer = await inquirer.prompt([
|
||||
{
|
||||
type: 'autocomplete',
|
||||
name: 'framework',
|
||||
message: 'Search for a framework:',
|
||||
source: (answersSoFar, input) => {
|
||||
const filtered = fuzzySearch(input, frameworks);
|
||||
return Promise.resolve(filtered);
|
||||
},
|
||||
pageSize: 10
|
||||
}
|
||||
]);
|
||||
|
||||
console.log(`\n✅ Selected: ${answer.framework}`);
|
||||
return answer;
|
||||
}
|
||||
|
||||
// Example: Command search with categories
|
||||
async function commandSearchExample() {
|
||||
const commands = [
|
||||
{ name: '📦 install - Install dependencies', value: 'install' },
|
||||
{ name: '🚀 start - Start development server', value: 'start' },
|
||||
{ name: '🏗️ build - Build for production', value: 'build' },
|
||||
{ name: '🧪 test - Run tests', value: 'test' },
|
||||
{ name: '🔍 lint - Check code quality', value: 'lint' },
|
||||
{ name: '✨ format - Format code', value: 'format' },
|
||||
{ name: '📝 generate - Generate files', value: 'generate' },
|
||||
{ name: '🔄 update - Update dependencies', value: 'update' },
|
||||
{ name: '🧹 clean - Clean build artifacts', value: 'clean' },
|
||||
{ name: '🚢 deploy - Deploy application', value: 'deploy' },
|
||||
{ name: '📊 analyze - Analyze bundle size', value: 'analyze' },
|
||||
{ name: '🐛 debug - Start debugger', value: 'debug' }
|
||||
];
|
||||
|
||||
const answer = await inquirer.prompt([
|
||||
{
|
||||
type: 'autocomplete',
|
||||
name: 'command',
|
||||
message: 'Search for a command:',
|
||||
source: (answersSoFar, input) => {
|
||||
return Promise.resolve(fuzzySearch(input, commands));
|
||||
},
|
||||
pageSize: 12
|
||||
}
|
||||
]);
|
||||
|
||||
console.log(`\n✅ Running: ${answer.command}`);
|
||||
return answer;
|
||||
}
|
||||
|
||||
// Example: Dynamic API search (simulated)
|
||||
async function apiSearchExample() {
|
||||
console.log('\n🔍 API Endpoint Search\n');
|
||||
|
||||
const answer = await inquirer.prompt([
|
||||
{
|
||||
type: 'autocomplete',
|
||||
name: 'endpoint',
|
||||
message: 'Search API endpoints:',
|
||||
source: async (answersSoFar, input) => {
|
||||
// Simulate API call
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
|
||||
const endpoints = [
|
||||
{ name: 'GET /users - List all users', value: '/users' },
|
||||
{ name: 'GET /users/:id - Get user by ID', value: '/users/:id' },
|
||||
{ name: 'POST /users - Create new user', value: '/users' },
|
||||
{ name: 'PUT /users/:id - Update user', value: '/users/:id' },
|
||||
{ name: 'DELETE /users/:id - Delete user', value: '/users/:id' },
|
||||
{ name: 'GET /posts - List all posts', value: '/posts' },
|
||||
{ name: 'GET /posts/:id - Get post by ID', value: '/posts/:id' },
|
||||
{ name: 'POST /posts - Create new post', value: '/posts' },
|
||||
{ name: 'GET /comments - List comments', value: '/comments' },
|
||||
{ name: 'POST /auth/login - User login', value: '/auth/login' },
|
||||
{ name: 'POST /auth/register - User registration', value: '/auth/register' },
|
||||
{ name: 'POST /auth/logout - User logout', value: '/auth/logout' }
|
||||
];
|
||||
|
||||
return fuzzySearch(input, endpoints);
|
||||
},
|
||||
pageSize: 10
|
||||
}
|
||||
]);
|
||||
|
||||
console.log(`\n✅ Selected endpoint: ${answer.endpoint}`);
|
||||
return answer;
|
||||
}
|
||||
|
||||
// Run if executed directly
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
(async () => {
|
||||
console.log('=== Autocomplete Examples ===\n');
|
||||
|
||||
console.log('1. Country & Package Selection');
|
||||
await autocompletePromptExample();
|
||||
|
||||
console.log('\n2. Framework Search');
|
||||
await frameworkSearchExample();
|
||||
|
||||
console.log('\n3. Command Search');
|
||||
await commandSearchExample();
|
||||
|
||||
console.log('\n4. API Endpoint Search');
|
||||
await apiSearchExample();
|
||||
|
||||
process.exit(0);
|
||||
})().catch((error) => {
|
||||
if (error.isTtyError) {
|
||||
console.error('❌ Prompt could not be rendered in this environment');
|
||||
} else {
|
||||
console.error('❌ User interrupted prompt');
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
export { autocompletePromptExample, frameworkSearchExample, commandSearchExample, apiSearchExample };
|
||||
140
skills/inquirer-patterns/templates/nodejs/checkbox-prompt.js
Normal file
140
skills/inquirer-patterns/templates/nodejs/checkbox-prompt.js
Normal file
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* Checkbox Prompt Template
|
||||
*
|
||||
* Use for: Multiple selections from options
|
||||
* Features: Space to toggle, Enter to confirm
|
||||
*/
|
||||
|
||||
import inquirer from 'inquirer';
|
||||
|
||||
async function checkboxPromptExample() {
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'features',
|
||||
message: 'Select features to include:',
|
||||
choices: [
|
||||
'Authentication',
|
||||
'Authorization',
|
||||
'Database Integration',
|
||||
'API Documentation',
|
||||
'Testing Suite',
|
||||
'CI/CD Pipeline',
|
||||
'Monitoring',
|
||||
'Logging'
|
||||
],
|
||||
validate: (choices) => {
|
||||
if (choices.length === 0) {
|
||||
return 'You must select at least one feature';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'tools',
|
||||
message: 'Select development tools:',
|
||||
choices: [
|
||||
{ name: 'ESLint (Linting)', value: 'eslint', checked: true },
|
||||
{ name: 'Prettier (Formatting)', value: 'prettier', checked: true },
|
||||
{ name: 'Jest (Testing)', value: 'jest' },
|
||||
{ name: 'Husky (Git Hooks)', value: 'husky' },
|
||||
{ name: 'TypeDoc (Documentation)', value: 'typedoc' },
|
||||
{ name: 'Webpack (Bundling)', value: 'webpack' }
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'plugins',
|
||||
message: 'Select plugins to install:',
|
||||
choices: [
|
||||
new inquirer.Separator('=== Essential ==='),
|
||||
{ name: 'dotenv - Environment variables', value: 'dotenv', checked: true },
|
||||
{ name: 'axios - HTTP client', value: 'axios', checked: true },
|
||||
new inquirer.Separator('=== Utilities ==='),
|
||||
{ name: 'lodash - Utility functions', value: 'lodash' },
|
||||
{ name: 'dayjs - Date manipulation', value: 'dayjs' },
|
||||
{ name: 'uuid - Unique IDs', value: 'uuid' },
|
||||
new inquirer.Separator('=== Validation ==='),
|
||||
{ name: 'joi - Schema validation', value: 'joi' },
|
||||
{ name: 'zod - TypeScript-first validation', value: 'zod' },
|
||||
new inquirer.Separator('=== Advanced ==='),
|
||||
{ name: 'bull - Job queues', value: 'bull' },
|
||||
{ name: 'socket.io - WebSockets', value: 'socket.io' }
|
||||
],
|
||||
pageSize: 15,
|
||||
validate: (choices) => {
|
||||
if (choices.length > 10) {
|
||||
return 'Please select no more than 10 plugins to avoid bloat';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'permissions',
|
||||
message: 'Grant the following permissions:',
|
||||
choices: [
|
||||
{ name: '📁 Read files', value: 'read', checked: true },
|
||||
{ name: '✏️ Write files', value: 'write' },
|
||||
{ name: '🗑️ Delete files', value: 'delete' },
|
||||
{ name: '🌐 Network access', value: 'network', checked: true },
|
||||
{ name: '🖥️ System commands', value: 'system' },
|
||||
{ name: '🔒 Keychain access', value: 'keychain' }
|
||||
],
|
||||
validate: (choices) => {
|
||||
if (choices.includes('delete') && !choices.includes('write')) {
|
||||
return 'Delete permission requires write permission';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'environments',
|
||||
message: 'Select deployment environments:',
|
||||
choices: [
|
||||
{ name: 'Development', value: 'dev', checked: true },
|
||||
{ name: 'Staging', value: 'staging' },
|
||||
{ name: 'Production', value: 'prod' },
|
||||
{ name: 'Testing', value: 'test', checked: true }
|
||||
],
|
||||
validate: (choices) => {
|
||||
if (!choices.includes('dev')) {
|
||||
return 'Development environment is required';
|
||||
}
|
||||
if (choices.includes('prod') && !choices.includes('staging')) {
|
||||
return 'Staging environment is recommended before production';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
console.log('\n✅ Selected options:');
|
||||
console.log(JSON.stringify(answers, null, 2));
|
||||
|
||||
// Example: Process selections
|
||||
console.log('\n📦 Installing selected features...');
|
||||
answers.features.forEach(feature => {
|
||||
console.log(` - ${feature}`);
|
||||
});
|
||||
|
||||
return answers;
|
||||
}
|
||||
|
||||
// Run if executed directly
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
checkboxPromptExample()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
if (error.isTtyError) {
|
||||
console.error('❌ Prompt could not be rendered in this environment');
|
||||
} else {
|
||||
console.error('❌ User interrupted prompt');
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
export { checkboxPromptExample };
|
||||
@@ -0,0 +1,456 @@
|
||||
/**
|
||||
* Comprehensive CLI Example
|
||||
*
|
||||
* Complete project initialization wizard combining all prompt types:
|
||||
* - Text input with validation
|
||||
* - List selections
|
||||
* - Checkbox selections
|
||||
* - Password input
|
||||
* - Autocomplete (optional)
|
||||
* - Conditional logic
|
||||
*/
|
||||
|
||||
import inquirer from 'inquirer';
|
||||
|
||||
async function projectInitWizard() {
|
||||
console.log(`
|
||||
╔═══════════════════════════════════════════════════════════╗
|
||||
║ ║
|
||||
║ 🚀 Project Initialization Wizard 🚀 ║
|
||||
║ ║
|
||||
╚═══════════════════════════════════════════════════════════╝
|
||||
`);
|
||||
|
||||
const config = await inquirer.prompt([
|
||||
// === PROJECT BASICS ===
|
||||
{
|
||||
type: 'input',
|
||||
name: 'projectName',
|
||||
message: '📦 Project name:',
|
||||
validate: (input) => {
|
||||
if (input.length === 0) return 'Project name is required';
|
||||
if (!/^[a-z0-9-_]+$/.test(input)) {
|
||||
return 'Use lowercase letters, numbers, hyphens, and underscores only';
|
||||
}
|
||||
if (input.length < 3) return 'Project name must be at least 3 characters';
|
||||
return true;
|
||||
},
|
||||
transformer: (input) => input.toLowerCase()
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'description',
|
||||
message: '📝 Project description:',
|
||||
validate: (input) => input.length > 0 || 'Description is required'
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'version',
|
||||
message: '🏷️ Initial version:',
|
||||
default: '0.1.0',
|
||||
validate: (input) => {
|
||||
return /^\d+\.\d+\.\d+$/.test(input) || 'Use semantic versioning (e.g., 0.1.0)';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'author',
|
||||
message: '👤 Author name:',
|
||||
default: process.env.USER || ''
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'email',
|
||||
message: '📧 Author email:',
|
||||
validate: (input) => {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return emailRegex.test(input) || 'Invalid email address';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'license',
|
||||
message: '📜 License:',
|
||||
choices: ['MIT', 'Apache-2.0', 'GPL-3.0', 'BSD-3-Clause', 'ISC', 'Unlicensed'],
|
||||
default: 'MIT'
|
||||
},
|
||||
|
||||
// === TECHNOLOGY STACK ===
|
||||
{
|
||||
type: 'list',
|
||||
name: 'projectType',
|
||||
message: '🛠️ Project type:',
|
||||
choices: [
|
||||
'Web Application',
|
||||
'CLI Tool',
|
||||
'API/Backend',
|
||||
'Library/Package',
|
||||
'Desktop Application',
|
||||
'Mobile Application'
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'language',
|
||||
message: '💻 Programming language:',
|
||||
choices: [
|
||||
{ name: 'TypeScript (Recommended)', value: 'typescript', short: 'TS' },
|
||||
{ name: 'JavaScript', value: 'javascript', short: 'JS' },
|
||||
{ name: 'Python', value: 'python', short: 'Py' },
|
||||
{ name: 'Go', value: 'go', short: 'Go' },
|
||||
{ name: 'Rust', value: 'rust', short: 'Rust' }
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'framework',
|
||||
message: '🎨 Framework/Runtime:',
|
||||
choices: (answers) => {
|
||||
const frameworks = {
|
||||
typescript: ['Next.js', 'Remix', 'Nest.js', 'Express', 'Fastify', 'Node.js'],
|
||||
javascript: ['React', 'Vue', 'Svelte', 'Express', 'Fastify', 'Node.js'],
|
||||
python: ['FastAPI', 'Django', 'Flask', 'Tornado', 'Sanic'],
|
||||
go: ['Gin', 'Echo', 'Fiber', 'Standard library'],
|
||||
rust: ['Actix', 'Rocket', 'Axum', 'Warp']
|
||||
};
|
||||
return frameworks[answers.language] || ['None'];
|
||||
}
|
||||
},
|
||||
|
||||
// === FEATURES & TOOLS ===
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'features',
|
||||
message: '✨ Select features:',
|
||||
choices: (answers) => {
|
||||
const baseFeatures = [
|
||||
{ name: 'Environment variables (.env)', value: 'env', checked: true },
|
||||
{ name: 'Configuration management', value: 'config', checked: true },
|
||||
{ name: 'Logging', value: 'logging', checked: true },
|
||||
{ name: 'Error handling', value: 'error-handling', checked: true }
|
||||
];
|
||||
|
||||
if (answers.projectType === 'Web Application' || answers.projectType === 'API/Backend') {
|
||||
baseFeatures.push(
|
||||
{ name: 'Authentication', value: 'auth' },
|
||||
{ name: 'Database integration', value: 'database' },
|
||||
{ name: 'API documentation', value: 'api-docs' },
|
||||
{ name: 'CORS handling', value: 'cors' }
|
||||
);
|
||||
}
|
||||
|
||||
if (answers.projectType === 'CLI Tool') {
|
||||
baseFeatures.push(
|
||||
{ name: 'Command-line arguments parser', value: 'cli-parser', checked: true },
|
||||
{ name: 'Interactive prompts', value: 'prompts', checked: true },
|
||||
{ name: 'Progress bars', value: 'progress' }
|
||||
);
|
||||
}
|
||||
|
||||
return baseFeatures;
|
||||
},
|
||||
validate: (choices) => choices.length > 0 || 'Select at least one feature'
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'devTools',
|
||||
message: '🔧 Development tools:',
|
||||
choices: (answers) => {
|
||||
const tools = [];
|
||||
|
||||
if (['typescript', 'javascript'].includes(answers.language)) {
|
||||
tools.push(
|
||||
{ name: 'ESLint - Linting', value: 'eslint', checked: true },
|
||||
{ name: 'Prettier - Code formatting', value: 'prettier', checked: true },
|
||||
{ name: 'Husky - Git hooks', value: 'husky' },
|
||||
{ name: 'Jest - Testing framework', value: 'jest', checked: true },
|
||||
{ name: 'TypeDoc/JSDoc - Documentation', value: 'docs' }
|
||||
);
|
||||
} else if (answers.language === 'python') {
|
||||
tools.push(
|
||||
{ name: 'Black - Code formatting', value: 'black', checked: true },
|
||||
{ name: 'Flake8 - Linting', value: 'flake8', checked: true },
|
||||
{ name: 'mypy - Type checking', value: 'mypy' },
|
||||
{ name: 'pytest - Testing framework', value: 'pytest', checked: true },
|
||||
{ name: 'Sphinx - Documentation', value: 'sphinx' }
|
||||
);
|
||||
}
|
||||
|
||||
return tools;
|
||||
},
|
||||
default: ['eslint', 'prettier', 'jest']
|
||||
},
|
||||
|
||||
// === DATABASE CONFIGURATION ===
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'useDatabase',
|
||||
message: '🗄️ Use database?',
|
||||
default: (answers) => {
|
||||
return answers.features.includes('database') ||
|
||||
['Web Application', 'API/Backend'].includes(answers.projectType);
|
||||
},
|
||||
when: (answers) => ['Web Application', 'API/Backend', 'CLI Tool'].includes(answers.projectType)
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'databaseType',
|
||||
message: '📊 Database type:',
|
||||
choices: [
|
||||
{ name: '🐘 PostgreSQL (Relational)', value: 'postgresql' },
|
||||
{ name: '🐬 MySQL (Relational)', value: 'mysql' },
|
||||
{ name: '🍃 MongoDB (Document)', value: 'mongodb' },
|
||||
{ name: '⚡ Redis (Key-Value)', value: 'redis' },
|
||||
{ name: '📁 SQLite (Embedded)', value: 'sqlite' },
|
||||
{ name: '🔥 Supabase (PostgreSQL + APIs)', value: 'supabase' }
|
||||
],
|
||||
when: (answers) => answers.useDatabase
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'databaseORM',
|
||||
message: '🔗 ORM/Database client:',
|
||||
choices: (answers) => {
|
||||
const orms = {
|
||||
typescript: {
|
||||
postgresql: ['Prisma', 'TypeORM', 'Kysely', 'Drizzle'],
|
||||
mysql: ['Prisma', 'TypeORM', 'Kysely', 'Drizzle'],
|
||||
mongodb: ['Mongoose', 'Prisma', 'TypeORM'],
|
||||
sqlite: ['Prisma', 'TypeORM', 'Better-SQLite3'],
|
||||
supabase: ['Supabase Client', 'Prisma']
|
||||
},
|
||||
python: {
|
||||
postgresql: ['SQLAlchemy', 'Django ORM', 'Tortoise ORM'],
|
||||
mysql: ['SQLAlchemy', 'Django ORM', 'Tortoise ORM'],
|
||||
mongodb: ['Motor', 'PyMongo', 'MongoEngine'],
|
||||
sqlite: ['SQLAlchemy', 'Django ORM']
|
||||
}
|
||||
};
|
||||
|
||||
const lang = answers.language;
|
||||
const db = answers.databaseType;
|
||||
return orms[lang]?.[db] || ['None'];
|
||||
},
|
||||
when: (answers) => answers.useDatabase && answers.databaseType !== 'redis'
|
||||
},
|
||||
|
||||
// === TESTING CONFIGURATION ===
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'setupTesting',
|
||||
message: '🧪 Setup testing?',
|
||||
default: true
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'testTypes',
|
||||
message: '🔬 Test types:',
|
||||
choices: [
|
||||
{ name: 'Unit tests', value: 'unit', checked: true },
|
||||
{ name: 'Integration tests', value: 'integration', checked: true },
|
||||
{ name: 'E2E tests', value: 'e2e' },
|
||||
{ name: 'Performance tests', value: 'performance' }
|
||||
],
|
||||
when: (answers) => answers.setupTesting
|
||||
},
|
||||
|
||||
// === CI/CD CONFIGURATION ===
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'setupCICD',
|
||||
message: '⚙️ Setup CI/CD?',
|
||||
default: true
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'cicdProvider',
|
||||
message: '🔄 CI/CD provider:',
|
||||
choices: ['GitHub Actions', 'GitLab CI', 'CircleCI', 'Jenkins', 'None'],
|
||||
when: (answers) => answers.setupCICD
|
||||
},
|
||||
|
||||
// === DEPLOYMENT CONFIGURATION ===
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'setupDeployment',
|
||||
message: '🚀 Setup deployment?',
|
||||
default: (answers) => answers.projectType !== 'Library/Package'
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'deploymentPlatform',
|
||||
message: '☁️ Deployment platform:',
|
||||
choices: (answers) => {
|
||||
if (answers.projectType === 'Web Application') {
|
||||
return ['Vercel', 'Netlify', 'AWS', 'Google Cloud', 'Azure', 'Self-hosted'];
|
||||
} else if (answers.projectType === 'API/Backend') {
|
||||
return ['AWS', 'Google Cloud', 'Azure', 'DigitalOcean', 'Heroku', 'Self-hosted'];
|
||||
} else if (answers.projectType === 'CLI Tool') {
|
||||
return ['npm', 'PyPI', 'Homebrew', 'Binary releases', 'Docker'];
|
||||
}
|
||||
return ['AWS', 'Google Cloud', 'Azure', 'Self-hosted'];
|
||||
},
|
||||
when: (answers) => answers.setupDeployment
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'useDocker',
|
||||
message: '🐳 Use Docker?',
|
||||
default: true,
|
||||
when: (answers) => {
|
||||
return answers.setupDeployment &&
|
||||
!['Vercel', 'Netlify'].includes(answers.deploymentPlatform);
|
||||
}
|
||||
},
|
||||
|
||||
// === MONITORING & OBSERVABILITY ===
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'setupMonitoring',
|
||||
message: '📊 Setup monitoring & observability?',
|
||||
default: (answers) => answers.projectType !== 'Library/Package'
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'monitoringTools',
|
||||
message: '📈 Monitoring tools:',
|
||||
choices: [
|
||||
{ name: 'Sentry - Error tracking', value: 'sentry' },
|
||||
{ name: 'DataDog - Full observability', value: 'datadog' },
|
||||
{ name: 'Prometheus - Metrics', value: 'prometheus' },
|
||||
{ name: 'Grafana - Dashboards', value: 'grafana' },
|
||||
{ name: 'New Relic - APM', value: 'newrelic' }
|
||||
],
|
||||
when: (answers) => answers.setupMonitoring
|
||||
},
|
||||
|
||||
// === DOCUMENTATION ===
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'generateDocs',
|
||||
message: '📚 Generate documentation?',
|
||||
default: true
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'docTypes',
|
||||
message: '📖 Documentation types:',
|
||||
choices: [
|
||||
{ name: 'README.md', value: 'readme', checked: true },
|
||||
{ name: 'API documentation', value: 'api', checked: true },
|
||||
{ name: 'Contributing guidelines', value: 'contributing' },
|
||||
{ name: 'Code of conduct', value: 'coc' },
|
||||
{ name: 'Changelog', value: 'changelog', checked: true }
|
||||
],
|
||||
when: (answers) => answers.generateDocs
|
||||
},
|
||||
|
||||
// === SECURITY ===
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'securitySetup',
|
||||
message: '🔒 Setup security features?',
|
||||
default: true,
|
||||
when: (answers) => ['Web Application', 'API/Backend'].includes(answers.projectType)
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'securityFeatures',
|
||||
message: '🛡️ Security features:',
|
||||
choices: [
|
||||
{ name: 'Dependency scanning', value: 'dep-scan', checked: true },
|
||||
{ name: 'Secret scanning', value: 'secret-scan', checked: true },
|
||||
{ name: 'HTTPS enforcement', value: 'https' },
|
||||
{ name: 'Rate limiting', value: 'rate-limit' },
|
||||
{ name: 'Input validation', value: 'validation', checked: true },
|
||||
{ name: 'Security headers', value: 'headers' }
|
||||
],
|
||||
when: (answers) => answers.securitySetup
|
||||
},
|
||||
|
||||
// === FINAL CONFIRMATION ===
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: '✅ Initialize project with these settings?',
|
||||
default: true
|
||||
}
|
||||
]);
|
||||
|
||||
if (!config.confirm) {
|
||||
console.log('\n❌ Project initialization cancelled.\n');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Display configuration summary
|
||||
console.log('\n' + '═'.repeat(60));
|
||||
console.log('📋 PROJECT CONFIGURATION SUMMARY');
|
||||
console.log('═'.repeat(60) + '\n');
|
||||
|
||||
console.log(`📦 Project: ${config.projectName} v${config.version}`);
|
||||
console.log(`📝 Description: ${config.description}`);
|
||||
console.log(`👤 Author: ${config.author} <${config.email}>`);
|
||||
console.log(`📜 License: ${config.license}\n`);
|
||||
|
||||
console.log(`💻 Language: ${config.language}`);
|
||||
console.log(`🎨 Framework: ${config.framework}`);
|
||||
console.log(`🛠️ Type: ${config.projectType}\n`);
|
||||
|
||||
if (config.useDatabase) {
|
||||
console.log(`🗄️ Database: ${config.databaseType}`);
|
||||
if (config.databaseORM) {
|
||||
console.log(`🔗 ORM: ${config.databaseORM}\n`);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.features.length > 0) {
|
||||
console.log(`✨ Features: ${config.features.join(', ')}`);
|
||||
}
|
||||
|
||||
if (config.devTools.length > 0) {
|
||||
console.log(`🔧 Dev Tools: ${config.devTools.join(', ')}\n`);
|
||||
}
|
||||
|
||||
if (config.setupDeployment) {
|
||||
console.log(`🚀 Deployment: ${config.deploymentPlatform}`);
|
||||
if (config.useDocker) console.log(`🐳 Docker: Enabled`);
|
||||
}
|
||||
|
||||
if (config.setupCICD) {
|
||||
console.log(`⚙️ CI/CD: ${config.cicdProvider}`);
|
||||
}
|
||||
|
||||
console.log('\n' + '═'.repeat(60) + '\n');
|
||||
|
||||
console.log('🎉 Configuration complete! Initializing project...\n');
|
||||
|
||||
// Here you would actually create the project files
|
||||
// This is just a demonstration
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
// Run if executed directly
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
projectInitWizard()
|
||||
.then((config) => {
|
||||
if (config) {
|
||||
console.log('✅ Project initialized successfully!\n');
|
||||
console.log('Next steps:');
|
||||
console.log(` 1. cd ${config.projectName}`);
|
||||
console.log(' 2. Install dependencies');
|
||||
console.log(' 3. Start development');
|
||||
}
|
||||
process.exit(0);
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.isTtyError) {
|
||||
console.error('❌ Prompt could not be rendered in this environment');
|
||||
} else {
|
||||
console.error('❌ User interrupted prompt');
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
export { projectInitWizard };
|
||||
460
skills/inquirer-patterns/templates/nodejs/conditional-prompt.js
Normal file
460
skills/inquirer-patterns/templates/nodejs/conditional-prompt.js
Normal file
@@ -0,0 +1,460 @@
|
||||
/**
|
||||
* Conditional Prompt Template
|
||||
*
|
||||
* Use for: Dynamic forms based on previous answers
|
||||
* Features: Skip logic, dependent questions, branching
|
||||
*/
|
||||
|
||||
import inquirer from 'inquirer';
|
||||
|
||||
async function conditionalPromptExample() {
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'useDatabase',
|
||||
message: 'Do you want to use a database?',
|
||||
default: true
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'databaseType',
|
||||
message: 'Select database type:',
|
||||
choices: ['PostgreSQL', 'MySQL', 'MongoDB', 'SQLite', 'Redis'],
|
||||
when: (answers) => answers.useDatabase
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'databaseHost',
|
||||
message: 'Database host:',
|
||||
default: 'localhost',
|
||||
when: (answers) => {
|
||||
return answers.useDatabase && answers.databaseType !== 'SQLite';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'databasePort',
|
||||
message: 'Database port:',
|
||||
default: (answers) => {
|
||||
const ports = {
|
||||
'PostgreSQL': '5432',
|
||||
'MySQL': '3306',
|
||||
'MongoDB': '27017',
|
||||
'Redis': '6379'
|
||||
};
|
||||
return ports[answers.databaseType] || '5432';
|
||||
},
|
||||
when: (answers) => {
|
||||
return answers.useDatabase && answers.databaseType !== 'SQLite';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'databaseName',
|
||||
message: 'Database name:',
|
||||
when: (answers) => answers.useDatabase,
|
||||
validate: (input) => input.length > 0 || 'Database name required'
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'useAuthentication',
|
||||
message: 'Do you want to use authentication?',
|
||||
default: true,
|
||||
when: (answers) => answers.useDatabase
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'databaseUsername',
|
||||
message: 'Database username:',
|
||||
when: (answers) => answers.useDatabase && answers.useAuthentication,
|
||||
validate: (input) => input.length > 0 || 'Username required'
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'databasePassword',
|
||||
message: 'Database password:',
|
||||
mask: '*',
|
||||
when: (answers) => answers.useDatabase && answers.useAuthentication
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'useSSL',
|
||||
message: 'Use SSL connection?',
|
||||
default: false,
|
||||
when: (answers) => {
|
||||
return answers.useDatabase &&
|
||||
answers.databaseType !== 'SQLite' &&
|
||||
answers.databaseHost !== 'localhost';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'sslCertPath',
|
||||
message: 'Path to SSL certificate:',
|
||||
when: (answers) => answers.useSSL,
|
||||
validate: (input) => input.length > 0 || 'SSL certificate path required'
|
||||
}
|
||||
]);
|
||||
|
||||
console.log('\n✅ Configuration:');
|
||||
console.log(JSON.stringify(answers, null, 2));
|
||||
|
||||
return answers;
|
||||
}
|
||||
|
||||
// Example: Deployment configuration wizard
|
||||
async function deploymentWizard() {
|
||||
console.log('\n🚀 Deployment Configuration Wizard\n');
|
||||
|
||||
const config = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'environment',
|
||||
message: 'Select deployment environment:',
|
||||
choices: ['Development', 'Staging', 'Production']
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'useDocker',
|
||||
message: 'Deploy using Docker?',
|
||||
default: true
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'dockerImage',
|
||||
message: 'Docker image name:',
|
||||
when: (answers) => answers.useDocker,
|
||||
default: 'myapp:latest',
|
||||
validate: (input) => /^[a-z0-9-_/:.]+$/.test(input) || 'Invalid Docker image name'
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'registry',
|
||||
message: 'Container registry:',
|
||||
choices: ['Docker Hub', 'GitHub Container Registry', 'AWS ECR', 'Google Artifact Registry'],
|
||||
when: (answers) => answers.useDocker
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'platform',
|
||||
message: 'Deployment platform:',
|
||||
choices: ['AWS', 'Google Cloud', 'Azure', 'DigitalOcean', 'Vercel', 'Netlify', 'Self-hosted']
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'awsService',
|
||||
message: 'AWS service:',
|
||||
choices: ['ECS', 'EKS', 'Lambda', 'Elastic Beanstalk', 'EC2'],
|
||||
when: (answers) => answers.platform === 'AWS'
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'gcpService',
|
||||
message: 'Google Cloud service:',
|
||||
choices: ['Cloud Run', 'GKE', 'App Engine', 'Compute Engine'],
|
||||
when: (answers) => answers.platform === 'Google Cloud'
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'autoScale',
|
||||
message: 'Enable auto-scaling?',
|
||||
default: true,
|
||||
when: (answers) => {
|
||||
const scalableServices = ['ECS', 'EKS', 'Cloud Run', 'GKE'];
|
||||
return scalableServices.includes(answers.awsService) ||
|
||||
scalableServices.includes(answers.gcpService);
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'minInstances',
|
||||
message: 'Minimum instances:',
|
||||
default: '1',
|
||||
when: (answers) => answers.autoScale,
|
||||
validate: (input) => {
|
||||
const num = parseInt(input);
|
||||
return num > 0 || 'Must be at least 1';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'maxInstances',
|
||||
message: 'Maximum instances:',
|
||||
default: '10',
|
||||
when: (answers) => answers.autoScale,
|
||||
validate: (input, answers) => {
|
||||
const num = parseInt(input);
|
||||
const min = parseInt(answers.minInstances);
|
||||
return num >= min || `Must be at least ${min}`;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'useCDN',
|
||||
message: 'Use CDN for static assets?',
|
||||
default: true,
|
||||
when: (answers) => answers.environment === 'Production'
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'cdnProvider',
|
||||
message: 'CDN provider:',
|
||||
choices: ['CloudFlare', 'AWS CloudFront', 'Google Cloud CDN', 'Azure CDN'],
|
||||
when: (answers) => answers.useCDN
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'setupMonitoring',
|
||||
message: 'Setup monitoring?',
|
||||
default: true
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'monitoringTools',
|
||||
message: 'Select monitoring tools:',
|
||||
choices: ['Prometheus', 'Grafana', 'Datadog', 'New Relic', 'Sentry'],
|
||||
when: (answers) => answers.setupMonitoring,
|
||||
validate: (choices) => choices.length > 0 || 'Select at least one tool'
|
||||
}
|
||||
]);
|
||||
|
||||
console.log('\n✅ Deployment configuration complete!');
|
||||
console.log(JSON.stringify(config, null, 2));
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
// Example: Feature flag configuration
|
||||
async function featureFlagWizard() {
|
||||
console.log('\n🎛️ Feature Flag Configuration\n');
|
||||
|
||||
const config = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'featureName',
|
||||
message: 'Feature name:',
|
||||
validate: (input) => /^[a-z-_]+$/.test(input) || 'Use lowercase, hyphens, underscores only'
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'enabledByDefault',
|
||||
message: 'Enabled by default?',
|
||||
default: false
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'rolloutStrategy',
|
||||
message: 'Rollout strategy:',
|
||||
choices: [
|
||||
'All users',
|
||||
'Percentage rollout',
|
||||
'User targeting',
|
||||
'Beta users only',
|
||||
'Manual control'
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'rolloutPercentage',
|
||||
message: 'Rollout percentage (0-100):',
|
||||
when: (answers) => answers.rolloutStrategy === 'Percentage rollout',
|
||||
default: '10',
|
||||
validate: (input) => {
|
||||
const num = parseInt(input);
|
||||
return (num >= 0 && num <= 100) || 'Must be between 0 and 100';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'targetUserGroups',
|
||||
message: 'Target user groups:',
|
||||
choices: ['Beta testers', 'Premium users', 'Internal team', 'Early adopters', 'Specific regions'],
|
||||
when: (answers) => answers.rolloutStrategy === 'User targeting',
|
||||
validate: (choices) => choices.length > 0 || 'Select at least one group'
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'targetRegions',
|
||||
message: 'Target regions:',
|
||||
choices: ['North America', 'Europe', 'Asia Pacific', 'South America', 'Africa'],
|
||||
when: (answers) => {
|
||||
return answers.rolloutStrategy === 'User targeting' &&
|
||||
answers.targetUserGroups.includes('Specific regions');
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'enableMetrics',
|
||||
message: 'Track feature usage metrics?',
|
||||
default: true
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'metrics',
|
||||
message: 'Select metrics to track:',
|
||||
choices: [
|
||||
'Usage count',
|
||||
'User adoption rate',
|
||||
'Performance impact',
|
||||
'Error rate',
|
||||
'User feedback'
|
||||
],
|
||||
when: (answers) => answers.enableMetrics
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'addExpirationDate',
|
||||
message: 'Set feature flag expiration?',
|
||||
default: false
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'expirationDate',
|
||||
message: 'Expiration date (YYYY-MM-DD):',
|
||||
when: (answers) => answers.addExpirationDate,
|
||||
validate: (input) => {
|
||||
const date = new Date(input);
|
||||
if (isNaN(date.getTime())) return 'Invalid date format';
|
||||
if (date < new Date()) return 'Date must be in the future';
|
||||
return true;
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
console.log('\n✅ Feature flag configured!');
|
||||
console.log(JSON.stringify(config, null, 2));
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
// Example: CI/CD pipeline setup
|
||||
async function cicdPipelineWizard() {
|
||||
console.log('\n⚙️ CI/CD Pipeline Configuration\n');
|
||||
|
||||
const config = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'provider',
|
||||
message: 'CI/CD provider:',
|
||||
choices: ['GitHub Actions', 'GitLab CI', 'CircleCI', 'Jenkins', 'Travis CI']
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'triggers',
|
||||
message: 'Pipeline triggers:',
|
||||
choices: [
|
||||
'Push to main/master',
|
||||
'Pull request',
|
||||
'Tag creation',
|
||||
'Manual trigger',
|
||||
'Scheduled (cron)'
|
||||
],
|
||||
default: ['Push to main/master', 'Pull request']
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'cronSchedule',
|
||||
message: 'Cron schedule:',
|
||||
when: (answers) => answers.triggers.includes('Scheduled (cron)'),
|
||||
default: '0 2 * * *',
|
||||
validate: (input) => {
|
||||
// Basic cron validation
|
||||
const parts = input.split(' ');
|
||||
return parts.length === 5 || 'Invalid cron format (5 parts required)';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'stages',
|
||||
message: 'Pipeline stages:',
|
||||
choices: ['Build', 'Test', 'Lint', 'Security scan', 'Deploy'],
|
||||
default: ['Build', 'Test', 'Deploy'],
|
||||
validate: (choices) => choices.length > 0 || 'Select at least one stage'
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'testTypes',
|
||||
message: 'Test types to run:',
|
||||
choices: ['Unit tests', 'Integration tests', 'E2E tests', 'Performance tests'],
|
||||
when: (answers) => answers.stages.includes('Test')
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'securityTools',
|
||||
message: 'Security scanning tools:',
|
||||
choices: ['Snyk', 'Dependabot', 'SonarQube', 'OWASP Dependency Check'],
|
||||
when: (answers) => answers.stages.includes('Security scan')
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'deployEnvironments',
|
||||
message: 'Deployment environments:',
|
||||
choices: ['Development', 'Staging', 'Production'],
|
||||
when: (answers) => answers.stages.includes('Deploy'),
|
||||
default: ['Staging', 'Production']
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'requireApproval',
|
||||
message: 'Require manual approval for production?',
|
||||
default: true,
|
||||
when: (answers) => {
|
||||
return answers.stages.includes('Deploy') &&
|
||||
answers.deployEnvironments?.includes('Production');
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'enableNotifications',
|
||||
message: 'Enable build notifications?',
|
||||
default: true
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'notificationChannels',
|
||||
message: 'Notification channels:',
|
||||
choices: ['Email', 'Slack', 'Discord', 'Microsoft Teams'],
|
||||
when: (answers) => answers.enableNotifications
|
||||
}
|
||||
]);
|
||||
|
||||
console.log('\n✅ CI/CD pipeline configured!');
|
||||
console.log(JSON.stringify(config, null, 2));
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
// Run if executed directly
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
(async () => {
|
||||
console.log('=== Conditional Prompt Examples ===\n');
|
||||
|
||||
console.log('1. Database Configuration');
|
||||
await conditionalPromptExample();
|
||||
|
||||
console.log('\n2. Deployment Wizard');
|
||||
await deploymentWizard();
|
||||
|
||||
console.log('\n3. Feature Flag Configuration');
|
||||
await featureFlagWizard();
|
||||
|
||||
console.log('\n4. CI/CD Pipeline Setup');
|
||||
await cicdPipelineWizard();
|
||||
|
||||
process.exit(0);
|
||||
})().catch((error) => {
|
||||
if (error.isTtyError) {
|
||||
console.error('❌ Prompt could not be rendered in this environment');
|
||||
} else {
|
||||
console.error('❌ User interrupted prompt');
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
conditionalPromptExample,
|
||||
deploymentWizard,
|
||||
featureFlagWizard,
|
||||
cicdPipelineWizard
|
||||
};
|
||||
104
skills/inquirer-patterns/templates/nodejs/list-prompt.js
Normal file
104
skills/inquirer-patterns/templates/nodejs/list-prompt.js
Normal file
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* List Selection Prompt Template
|
||||
*
|
||||
* Use for: Single choice from predefined options
|
||||
* Features: Arrow key navigation, search filtering
|
||||
*/
|
||||
|
||||
import inquirer from 'inquirer';
|
||||
|
||||
async function listPromptExample() {
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'framework',
|
||||
message: 'Choose your preferred framework:',
|
||||
choices: [
|
||||
'React',
|
||||
'Vue',
|
||||
'Angular',
|
||||
'Svelte',
|
||||
'Next.js',
|
||||
'Nuxt.js'
|
||||
],
|
||||
default: 'React'
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'language',
|
||||
message: 'Choose programming language:',
|
||||
choices: [
|
||||
{ name: 'JavaScript', value: 'js' },
|
||||
{ name: 'TypeScript', value: 'ts' },
|
||||
{ name: 'Python', value: 'py' },
|
||||
{ name: 'Ruby', value: 'rb' },
|
||||
{ name: 'Go', value: 'go' }
|
||||
],
|
||||
default: 'ts'
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'packageManager',
|
||||
message: 'Choose package manager:',
|
||||
choices: [
|
||||
{ name: 'npm (Node Package Manager)', value: 'npm', short: 'npm' },
|
||||
{ name: 'yarn (Fast, reliable package manager)', value: 'yarn', short: 'yarn' },
|
||||
{ name: 'pnpm (Fast, disk space efficient)', value: 'pnpm', short: 'pnpm' },
|
||||
{ name: 'bun (All-in-one toolkit)', value: 'bun', short: 'bun' }
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'environment',
|
||||
message: 'Select deployment environment:',
|
||||
choices: [
|
||||
new inquirer.Separator('--- Cloud Platforms ---'),
|
||||
'AWS',
|
||||
'Google Cloud',
|
||||
'Azure',
|
||||
new inquirer.Separator('--- Serverless ---'),
|
||||
'Vercel',
|
||||
'Netlify',
|
||||
'Cloudflare Workers',
|
||||
new inquirer.Separator('--- Self-hosted ---'),
|
||||
'Docker',
|
||||
'Kubernetes'
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'database',
|
||||
message: 'Choose database:',
|
||||
choices: [
|
||||
{ name: '🐘 PostgreSQL (Relational)', value: 'postgresql' },
|
||||
{ name: '🐬 MySQL (Relational)', value: 'mysql' },
|
||||
{ name: '🍃 MongoDB (Document)', value: 'mongodb' },
|
||||
{ name: '⚡ Redis (Key-Value)', value: 'redis' },
|
||||
{ name: '📊 SQLite (Embedded)', value: 'sqlite' },
|
||||
{ name: '🔥 Supabase (PostgreSQL + APIs)', value: 'supabase' }
|
||||
],
|
||||
pageSize: 10
|
||||
}
|
||||
]);
|
||||
|
||||
console.log('\n✅ Selections:');
|
||||
console.log(JSON.stringify(answers, null, 2));
|
||||
|
||||
return answers;
|
||||
}
|
||||
|
||||
// Run if executed directly
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
listPromptExample()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
if (error.isTtyError) {
|
||||
console.error('❌ Prompt could not be rendered in this environment');
|
||||
} else {
|
||||
console.error('❌ User interrupted prompt');
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
export { listPromptExample };
|
||||
208
skills/inquirer-patterns/templates/nodejs/password-prompt.js
Normal file
208
skills/inquirer-patterns/templates/nodejs/password-prompt.js
Normal file
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
* Password Prompt Template
|
||||
*
|
||||
* Use for: Sensitive input (credentials, tokens)
|
||||
* Features: Hidden input, confirmation, validation
|
||||
*/
|
||||
|
||||
import inquirer from 'inquirer';
|
||||
|
||||
async function passwordPromptExample() {
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'password',
|
||||
name: 'password',
|
||||
message: 'Enter your password:',
|
||||
mask: '*',
|
||||
validate: (input) => {
|
||||
if (input.length < 8) {
|
||||
return 'Password must be at least 8 characters';
|
||||
}
|
||||
if (!/[A-Z]/.test(input)) {
|
||||
return 'Password must contain at least one uppercase letter';
|
||||
}
|
||||
if (!/[a-z]/.test(input)) {
|
||||
return 'Password must contain at least one lowercase letter';
|
||||
}
|
||||
if (!/[0-9]/.test(input)) {
|
||||
return 'Password must contain at least one number';
|
||||
}
|
||||
if (!/[!@#$%^&*(),.?":{}|<>]/.test(input)) {
|
||||
return 'Password must contain at least one special character';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'confirmPassword',
|
||||
message: 'Confirm your password:',
|
||||
mask: '*',
|
||||
validate: (input, answers) => {
|
||||
if (input !== answers.password) {
|
||||
return 'Passwords do not match';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'apiKey',
|
||||
message: 'Enter your API key:',
|
||||
mask: '•',
|
||||
validate: (input) => {
|
||||
if (input.length === 0) {
|
||||
return 'API key is required';
|
||||
}
|
||||
// Example: Validate API key format (e.g., sk-...)
|
||||
if (!input.startsWith('sk-') && !input.startsWith('pk-')) {
|
||||
return 'API key must start with "sk-" or "pk-"';
|
||||
}
|
||||
if (input.length < 32) {
|
||||
return 'API key appears to be too short';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'oldPassword',
|
||||
message: 'Enter your old password (for password change):',
|
||||
mask: '*',
|
||||
when: (answers) => {
|
||||
// Only ask for old password if changing password
|
||||
return answers.password && answers.confirmPassword;
|
||||
},
|
||||
validate: (input) => {
|
||||
if (input.length === 0) {
|
||||
return 'Old password is required';
|
||||
}
|
||||
// In real app, you'd verify against stored password
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'encryptionKey',
|
||||
message: 'Enter encryption key (optional):',
|
||||
mask: '#',
|
||||
validate: (input) => {
|
||||
if (input.length === 0) return true; // Optional
|
||||
if (input.length < 16) {
|
||||
return 'Encryption key must be at least 16 characters';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
// Don't log actual passwords!
|
||||
console.log('\n✅ Credentials received (not displayed for security)');
|
||||
console.log('Password strength:', calculatePasswordStrength(answers.password));
|
||||
console.log('API key format:', answers.apiKey.substring(0, 6) + '...');
|
||||
|
||||
// In real app, you'd:
|
||||
// - Hash the password before storing
|
||||
// - Encrypt the API key
|
||||
// - Store securely (not in plain text)
|
||||
|
||||
return {
|
||||
passwordHash: hashPassword(answers.password),
|
||||
apiKeyEncrypted: encryptApiKey(answers.apiKey)
|
||||
};
|
||||
}
|
||||
|
||||
// Helper function to calculate password strength
|
||||
function calculatePasswordStrength(password) {
|
||||
let strength = 0;
|
||||
|
||||
if (password.length >= 8) strength++;
|
||||
if (password.length >= 12) strength++;
|
||||
if (/[a-z]/.test(password)) strength++;
|
||||
if (/[A-Z]/.test(password)) strength++;
|
||||
if (/[0-9]/.test(password)) strength++;
|
||||
if (/[!@#$%^&*(),.?":{}|<>]/.test(password)) strength++;
|
||||
|
||||
const levels = ['Very Weak', 'Weak', 'Fair', 'Good', 'Strong', 'Very Strong'];
|
||||
return levels[Math.min(strength, levels.length - 1)];
|
||||
}
|
||||
|
||||
// Placeholder for password hashing (use bcrypt in production)
|
||||
function hashPassword(password) {
|
||||
return `[HASHED:${password.length}_chars]`;
|
||||
}
|
||||
|
||||
// Placeholder for API key encryption (use proper encryption in production)
|
||||
function encryptApiKey(apiKey) {
|
||||
return `[ENCRYPTED:${apiKey.substring(0, 6)}...]`;
|
||||
}
|
||||
|
||||
// Example: Secure credential storage
|
||||
async function securePasswordExample() {
|
||||
console.log('\n🔐 Secure Password Setup\n');
|
||||
|
||||
const credentials = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'username',
|
||||
message: 'Username:',
|
||||
validate: (input) => input.length > 0 || 'Username required'
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'password',
|
||||
message: 'Password:',
|
||||
mask: '*',
|
||||
validate: (input) => {
|
||||
const issues = [];
|
||||
if (input.length < 12) issues.push('at least 12 characters');
|
||||
if (!/[A-Z]/.test(input)) issues.push('an uppercase letter');
|
||||
if (!/[a-z]/.test(input)) issues.push('a lowercase letter');
|
||||
if (!/[0-9]/.test(input)) issues.push('a number');
|
||||
if (!/[!@#$%^&*]/.test(input)) issues.push('a special character (!@#$%^&*)');
|
||||
|
||||
if (issues.length > 0) {
|
||||
return `Password must contain: ${issues.join(', ')}`;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'confirm',
|
||||
message: 'Confirm password:',
|
||||
mask: '*',
|
||||
validate: (input, answers) => {
|
||||
return input === answers.password || 'Passwords do not match';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'remember',
|
||||
message: 'Remember credentials? (stored securely)',
|
||||
default: false
|
||||
}
|
||||
]);
|
||||
|
||||
console.log(`\n✅ Account created for: ${credentials.username}`);
|
||||
console.log(`🔒 Password strength: ${calculatePasswordStrength(credentials.password)}`);
|
||||
|
||||
return credentials;
|
||||
}
|
||||
|
||||
// Run if executed directly
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
passwordPromptExample()
|
||||
.then(() => securePasswordExample())
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
if (error.isTtyError) {
|
||||
console.error('❌ Prompt could not be rendered in this environment');
|
||||
} else {
|
||||
console.error('❌ User interrupted prompt');
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
export { passwordPromptExample, securePasswordExample };
|
||||
105
skills/inquirer-patterns/templates/nodejs/text-prompt.js
Normal file
105
skills/inquirer-patterns/templates/nodejs/text-prompt.js
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* Text Input Prompt Template
|
||||
*
|
||||
* Use for: Names, emails, URLs, paths, free-form text
|
||||
* Features: Validation, default values, transform
|
||||
*/
|
||||
|
||||
import inquirer from 'inquirer';
|
||||
|
||||
async function textPromptExample() {
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'username',
|
||||
message: 'Enter your username:',
|
||||
default: '',
|
||||
validate: (input) => {
|
||||
if (input.length === 0) {
|
||||
return 'Username is required';
|
||||
}
|
||||
if (input.length < 3) {
|
||||
return 'Username must be at least 3 characters';
|
||||
}
|
||||
if (!/^[a-zA-Z0-9_-]+$/.test(input)) {
|
||||
return 'Username can only contain letters, numbers, hyphens, and underscores';
|
||||
}
|
||||
return true;
|
||||
},
|
||||
transformer: (input) => {
|
||||
return input.toLowerCase();
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'email',
|
||||
message: 'Enter your email:',
|
||||
validate: (input) => {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return emailRegex.test(input) || 'Invalid email address';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'website',
|
||||
message: 'Enter your website (optional):',
|
||||
default: '',
|
||||
validate: (input) => {
|
||||
if (input.length === 0) return true; // Optional field
|
||||
const urlRegex = /^https?:\/\/.+/;
|
||||
return urlRegex.test(input) || 'Must be a valid URL (http:// or https://)';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'age',
|
||||
message: 'Enter your age:',
|
||||
validate: (input) => {
|
||||
const age = parseInt(input);
|
||||
if (isNaN(age)) {
|
||||
return 'Please enter a valid number';
|
||||
}
|
||||
if (age < 18) {
|
||||
return 'You must be at least 18 years old';
|
||||
}
|
||||
if (age > 120) {
|
||||
return 'Please enter a realistic age';
|
||||
}
|
||||
return true;
|
||||
},
|
||||
filter: (input) => parseInt(input)
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'bio',
|
||||
message: 'Enter a short bio:',
|
||||
validate: (input) => {
|
||||
if (input.length > 200) {
|
||||
return 'Bio must be 200 characters or less';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
console.log('\n✅ Answers received:');
|
||||
console.log(JSON.stringify(answers, null, 2));
|
||||
|
||||
return answers;
|
||||
}
|
||||
|
||||
// Run if executed directly
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
textPromptExample()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
if (error.isTtyError) {
|
||||
console.error('❌ Prompt could not be rendered in this environment');
|
||||
} else {
|
||||
console.error('❌ User interrupted prompt');
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
export { textPromptExample };
|
||||
361
skills/inquirer-patterns/templates/python/autocomplete_prompt.py
Normal file
361
skills/inquirer-patterns/templates/python/autocomplete_prompt.py
Normal file
@@ -0,0 +1,361 @@
|
||||
"""
|
||||
Autocomplete Prompt Template
|
||||
|
||||
Use for: Large option lists with search
|
||||
Features: Type-ahead, fuzzy matching, suggestions
|
||||
"""
|
||||
|
||||
import questionary
|
||||
from questionary import Choice
|
||||
|
||||
|
||||
# Example: Countries list for autocomplete
|
||||
COUNTRIES = [
|
||||
'Afghanistan', 'Albania', 'Algeria', 'Andorra', 'Angola',
|
||||
'Argentina', 'Armenia', 'Australia', 'Austria', 'Azerbaijan',
|
||||
'Bahamas', 'Bahrain', 'Bangladesh', 'Barbados', 'Belarus',
|
||||
'Belgium', 'Belize', 'Benin', 'Bhutan', 'Bolivia',
|
||||
'Brazil', 'Brunei', 'Bulgaria', 'Burkina Faso', 'Burundi',
|
||||
'Cambodia', 'Cameroon', 'Canada', 'Cape Verde', 'Chad',
|
||||
'Chile', 'China', 'Colombia', 'Comoros', 'Congo',
|
||||
'Costa Rica', 'Croatia', 'Cuba', 'Cyprus', 'Czech Republic',
|
||||
'Denmark', 'Djibouti', 'Dominica', 'Dominican Republic',
|
||||
'Ecuador', 'Egypt', 'El Salvador', 'Estonia', 'Ethiopia',
|
||||
'Fiji', 'Finland', 'France', 'Gabon', 'Gambia',
|
||||
'Georgia', 'Germany', 'Ghana', 'Greece', 'Grenada',
|
||||
'Guatemala', 'Guinea', 'Guyana', 'Haiti', 'Honduras',
|
||||
'Hungary', 'Iceland', 'India', 'Indonesia', 'Iran',
|
||||
'Iraq', 'Ireland', 'Israel', 'Italy', 'Jamaica',
|
||||
'Japan', 'Jordan', 'Kazakhstan', 'Kenya', 'Kuwait',
|
||||
'Laos', 'Latvia', 'Lebanon', 'Lesotho', 'Liberia',
|
||||
'Libya', 'Lithuania', 'Luxembourg', 'Madagascar', 'Malawi',
|
||||
'Malaysia', 'Maldives', 'Mali', 'Malta', 'Mexico',
|
||||
'Moldova', 'Monaco', 'Mongolia', 'Morocco', 'Mozambique',
|
||||
'Myanmar', 'Namibia', 'Nepal', 'Netherlands', 'New Zealand',
|
||||
'Nicaragua', 'Niger', 'Nigeria', 'Norway', 'Oman',
|
||||
'Pakistan', 'Panama', 'Paraguay', 'Peru', 'Philippines',
|
||||
'Poland', 'Portugal', 'Qatar', 'Romania', 'Russia',
|
||||
'Rwanda', 'Saudi Arabia', 'Senegal', 'Serbia', 'Singapore',
|
||||
'Slovakia', 'Slovenia', 'Somalia', 'South Africa', 'South Korea',
|
||||
'Spain', 'Sri Lanka', 'Sudan', 'Sweden', 'Switzerland',
|
||||
'Syria', 'Taiwan', 'Tanzania', 'Thailand', 'Togo',
|
||||
'Tunisia', 'Turkey', 'Uganda', 'Ukraine', 'United Arab Emirates',
|
||||
'United Kingdom', 'United States', 'Uruguay', 'Uzbekistan',
|
||||
'Venezuela', 'Vietnam', 'Yemen', 'Zambia', 'Zimbabwe'
|
||||
]
|
||||
|
||||
# Example: Popular packages
|
||||
POPULAR_PACKAGES = [
|
||||
'express', 'react', 'vue', 'angular', 'next', 'nuxt',
|
||||
'axios', 'lodash', 'moment', 'dayjs', 'uuid', 'dotenv',
|
||||
'typescript', 'eslint', 'prettier', 'jest', 'mocha', 'chai',
|
||||
'webpack', 'vite', 'rollup', 'babel', 'esbuild',
|
||||
'socket.io', 'redis', 'mongodb', 'mongoose', 'sequelize',
|
||||
'prisma', 'typeorm', 'knex', 'pg', 'mysql2',
|
||||
'bcrypt', 'jsonwebtoken', 'passport', 'helmet', 'cors',
|
||||
'multer', 'sharp', 'puppeteer', 'playwright', 'cheerio'
|
||||
]
|
||||
|
||||
|
||||
def autocomplete_prompt_example():
|
||||
"""Example autocomplete prompts"""
|
||||
|
||||
print("\n🔍 Autocomplete Example\n")
|
||||
|
||||
# Country selection with autocomplete
|
||||
country = questionary.autocomplete(
|
||||
"Select your country:",
|
||||
choices=COUNTRIES,
|
||||
validate=lambda text: len(text) > 0 or "Please select a country"
|
||||
).ask()
|
||||
|
||||
# Package selection
|
||||
package = questionary.autocomplete(
|
||||
"Search for an npm package:",
|
||||
choices=POPULAR_PACKAGES
|
||||
).ask()
|
||||
|
||||
# Cities based on country (conditional)
|
||||
city = None
|
||||
cities_by_country = {
|
||||
'United States': ['New York', 'Los Angeles', 'Chicago', 'Houston', 'Phoenix'],
|
||||
'United Kingdom': ['London', 'Manchester', 'Birmingham', 'Glasgow', 'Liverpool'],
|
||||
'Canada': ['Toronto', 'Vancouver', 'Montreal', 'Calgary', 'Ottawa'],
|
||||
'Australia': ['Sydney', 'Melbourne', 'Brisbane', 'Perth', 'Adelaide'],
|
||||
'Germany': ['Berlin', 'Munich', 'Hamburg', 'Frankfurt', 'Cologne']
|
||||
}
|
||||
|
||||
if country in cities_by_country:
|
||||
city = questionary.autocomplete(
|
||||
"Select city:",
|
||||
choices=cities_by_country[country]
|
||||
).ask()
|
||||
|
||||
answers = {
|
||||
'country': country,
|
||||
'package': package,
|
||||
'city': city
|
||||
}
|
||||
|
||||
print("\n✅ Selections:")
|
||||
import json
|
||||
print(json.dumps(answers, indent=2))
|
||||
|
||||
return answers
|
||||
|
||||
|
||||
def framework_search_example():
|
||||
"""Example: Framework/library search with descriptions"""
|
||||
|
||||
print("\n🔎 Framework Search Example\n")
|
||||
|
||||
frameworks = [
|
||||
'React - UI library by Facebook',
|
||||
'Vue.js - Progressive JavaScript framework',
|
||||
'Angular - Platform for building web apps',
|
||||
'Svelte - Cybernetically enhanced web apps',
|
||||
'Next.js - React framework with SSR',
|
||||
'Nuxt.js - Vue.js framework with SSR',
|
||||
'Remix - Full stack web framework',
|
||||
'SvelteKit - Svelte framework',
|
||||
'Express - Fast Node.js web framework',
|
||||
'Fastify - Fast and low overhead web framework',
|
||||
'NestJS - Progressive Node.js framework',
|
||||
'Koa - Expressive middleware for Node.js'
|
||||
]
|
||||
|
||||
framework = questionary.autocomplete(
|
||||
"Search for a framework:",
|
||||
choices=frameworks
|
||||
).ask()
|
||||
|
||||
# Extract value (remove description)
|
||||
framework_name = framework.split(' - ')[0] if ' - ' in framework else framework
|
||||
|
||||
print(f"\n✅ Selected: {framework_name}")
|
||||
|
||||
return {'framework': framework_name}
|
||||
|
||||
|
||||
def command_search_example():
|
||||
"""Example: Command search with emojis and categories"""
|
||||
|
||||
print("\n⌨️ Command Search Example\n")
|
||||
|
||||
commands = [
|
||||
'📦 install - Install dependencies',
|
||||
'🚀 start - Start development server',
|
||||
'🏗️ build - Build for production',
|
||||
'🧪 test - Run tests',
|
||||
'🔍 lint - Check code quality',
|
||||
'✨ format - Format code',
|
||||
'📝 generate - Generate files',
|
||||
'🔄 update - Update dependencies',
|
||||
'🧹 clean - Clean build artifacts',
|
||||
'🚢 deploy - Deploy application',
|
||||
'📊 analyze - Analyze bundle size',
|
||||
'🐛 debug - Start debugger'
|
||||
]
|
||||
|
||||
command = questionary.autocomplete(
|
||||
"Search for a command:",
|
||||
choices=commands
|
||||
).ask()
|
||||
|
||||
# Extract command name
|
||||
command_name = command.split(' - ')[0].split(' ', 1)[1] if ' - ' in command else command
|
||||
|
||||
print(f"\n✅ Running: {command_name}")
|
||||
|
||||
return {'command': command_name}
|
||||
|
||||
|
||||
def api_endpoint_search():
|
||||
"""Example: API endpoint search"""
|
||||
|
||||
print("\n🔍 API Endpoint Search\n")
|
||||
|
||||
endpoints = [
|
||||
'GET /users - List all users',
|
||||
'GET /users/:id - Get user by ID',
|
||||
'POST /users - Create new user',
|
||||
'PUT /users/:id - Update user',
|
||||
'DELETE /users/:id - Delete user',
|
||||
'GET /posts - List all posts',
|
||||
'GET /posts/:id - Get post by ID',
|
||||
'POST /posts - Create new post',
|
||||
'GET /comments - List comments',
|
||||
'POST /auth/login - User login',
|
||||
'POST /auth/register - User registration',
|
||||
'POST /auth/logout - User logout'
|
||||
]
|
||||
|
||||
endpoint = questionary.autocomplete(
|
||||
"Search API endpoints:",
|
||||
choices=endpoints
|
||||
).ask()
|
||||
|
||||
# Extract endpoint path
|
||||
endpoint_path = endpoint.split(' - ')[0] if ' - ' in endpoint else endpoint
|
||||
|
||||
print(f"\n✅ Selected endpoint: {endpoint_path}")
|
||||
|
||||
return {'endpoint': endpoint_path}
|
||||
|
||||
|
||||
def technology_stack_selection():
|
||||
"""Example: Building technology stack with multiple autocomplete prompts"""
|
||||
|
||||
print("\n🛠️ Technology Stack Selection\n")
|
||||
|
||||
# Programming languages
|
||||
languages = [
|
||||
'JavaScript', 'TypeScript', 'Python', 'Go', 'Rust',
|
||||
'Java', 'C++', 'Ruby', 'PHP', 'Swift', 'Kotlin'
|
||||
]
|
||||
|
||||
language = questionary.autocomplete(
|
||||
"Choose programming language:",
|
||||
choices=languages
|
||||
).ask()
|
||||
|
||||
# Frameworks based on language
|
||||
frameworks_by_language = {
|
||||
'JavaScript': ['React', 'Vue', 'Angular', 'Svelte', 'Express', 'Fastify'],
|
||||
'TypeScript': ['Next.js', 'Nest.js', 'Angular', 'Remix', 'tRPC'],
|
||||
'Python': ['Django', 'Flask', 'FastAPI', 'Tornado', 'Sanic'],
|
||||
'Go': ['Gin', 'Echo', 'Fiber', 'Chi', 'Gorilla'],
|
||||
'Rust': ['Actix', 'Rocket', 'Axum', 'Warp', 'Tide'],
|
||||
'Java': ['Spring', 'Micronaut', 'Quarkus', 'Vert.x'],
|
||||
'Ruby': ['Ruby on Rails', 'Sinatra', 'Hanami']
|
||||
}
|
||||
|
||||
framework_choices = frameworks_by_language.get(language, ['None'])
|
||||
framework = questionary.autocomplete(
|
||||
f"Choose {language} framework:",
|
||||
choices=framework_choices
|
||||
).ask()
|
||||
|
||||
# Databases
|
||||
databases = [
|
||||
'PostgreSQL', 'MySQL', 'MongoDB', 'Redis', 'SQLite',
|
||||
'Cassandra', 'DynamoDB', 'CouchDB', 'Neo4j', 'InfluxDB'
|
||||
]
|
||||
|
||||
database = questionary.autocomplete(
|
||||
"Choose database:",
|
||||
choices=databases
|
||||
).ask()
|
||||
|
||||
# Cloud providers
|
||||
cloud_providers = [
|
||||
'AWS', 'Google Cloud', 'Azure', 'DigitalOcean',
|
||||
'Heroku', 'Vercel', 'Netlify', 'Cloudflare'
|
||||
]
|
||||
|
||||
cloud = questionary.autocomplete(
|
||||
"Choose cloud provider:",
|
||||
choices=cloud_providers
|
||||
).ask()
|
||||
|
||||
stack = {
|
||||
'language': language,
|
||||
'framework': framework,
|
||||
'database': database,
|
||||
'cloud': cloud
|
||||
}
|
||||
|
||||
print("\n✅ Technology Stack:")
|
||||
import json
|
||||
print(json.dumps(stack, indent=2))
|
||||
|
||||
return stack
|
||||
|
||||
|
||||
def file_path_autocomplete():
|
||||
"""Example: File path autocomplete (simulated)"""
|
||||
|
||||
print("\n📁 File Path Autocomplete Example\n")
|
||||
|
||||
# Common project directories
|
||||
directories = [
|
||||
'/home/user/projects/web-app',
|
||||
'/home/user/projects/api-server',
|
||||
'/home/user/projects/cli-tool',
|
||||
'/var/www/html',
|
||||
'/opt/applications',
|
||||
'~/Documents/code',
|
||||
'~/workspace/nodejs',
|
||||
'~/workspace/python'
|
||||
]
|
||||
|
||||
project_path = questionary.autocomplete(
|
||||
"Select project directory:",
|
||||
choices=directories
|
||||
).ask()
|
||||
|
||||
# Common config files
|
||||
config_files = [
|
||||
'package.json',
|
||||
'tsconfig.json',
|
||||
'jest.config.js',
|
||||
'webpack.config.js',
|
||||
'.env',
|
||||
'.gitignore',
|
||||
'README.md',
|
||||
'Dockerfile',
|
||||
'docker-compose.yml'
|
||||
]
|
||||
|
||||
config_file = questionary.autocomplete(
|
||||
"Select config file:",
|
||||
choices=config_files
|
||||
).ask()
|
||||
|
||||
result = {
|
||||
'projectPath': project_path,
|
||||
'configFile': config_file
|
||||
}
|
||||
|
||||
print("\n✅ Selected:")
|
||||
import json
|
||||
print(json.dumps(result, indent=2))
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
"""Run autocomplete prompt examples"""
|
||||
try:
|
||||
print("=== Autocomplete Examples ===")
|
||||
|
||||
# Example 1: Basic autocomplete
|
||||
autocomplete_prompt_example()
|
||||
|
||||
# Example 2: Framework search
|
||||
framework_search_example()
|
||||
|
||||
# Example 3: Command search
|
||||
command_search_example()
|
||||
|
||||
# Example 4: API endpoint search
|
||||
api_endpoint_search()
|
||||
|
||||
# Example 5: Technology stack
|
||||
technology_stack_selection()
|
||||
|
||||
# Example 6: File path autocomplete
|
||||
file_path_autocomplete()
|
||||
|
||||
print("\n✅ Autocomplete examples complete!")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n❌ User interrupted prompt")
|
||||
exit(1)
|
||||
except Exception as e:
|
||||
print(f"\n\n❌ Error: {e}")
|
||||
exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
310
skills/inquirer-patterns/templates/python/checkbox_prompt.py
Normal file
310
skills/inquirer-patterns/templates/python/checkbox_prompt.py
Normal file
@@ -0,0 +1,310 @@
|
||||
"""
|
||||
Checkbox Prompt Template
|
||||
|
||||
Use for: Multiple selections from options
|
||||
Features: Space to toggle, Enter to confirm
|
||||
"""
|
||||
|
||||
import questionary
|
||||
from questionary import Choice, Separator, ValidationError, Validator
|
||||
|
||||
|
||||
class MinimumChoicesValidator(Validator):
|
||||
"""Validator to ensure minimum number of choices selected"""
|
||||
|
||||
def __init__(self, minimum=1, message=None):
|
||||
self.minimum = minimum
|
||||
self.message = message or f"You must select at least {minimum} option(s)"
|
||||
|
||||
def validate(self, document):
|
||||
# document.text contains selected choices as list
|
||||
if len(document.text) < self.minimum:
|
||||
raise ValidationError(
|
||||
message=self.message,
|
||||
cursor_position=0
|
||||
)
|
||||
|
||||
|
||||
class MaximumChoicesValidator(Validator):
|
||||
"""Validator to ensure maximum number of choices selected"""
|
||||
|
||||
def __init__(self, maximum=10, message=None):
|
||||
self.maximum = maximum
|
||||
self.message = message or f"Please select no more than {maximum} options"
|
||||
|
||||
def validate(self, document):
|
||||
if len(document.text) > self.maximum:
|
||||
raise ValidationError(
|
||||
message=self.message,
|
||||
cursor_position=0
|
||||
)
|
||||
|
||||
|
||||
def checkbox_prompt_example():
|
||||
"""Example checkbox prompts"""
|
||||
|
||||
print("\n☑️ Checkbox Selection Example\n")
|
||||
|
||||
# Simple checkbox
|
||||
features = questionary.checkbox(
|
||||
"Select features to include:",
|
||||
choices=[
|
||||
'Authentication',
|
||||
'Authorization',
|
||||
'Database Integration',
|
||||
'API Documentation',
|
||||
'Testing Suite',
|
||||
'CI/CD Pipeline',
|
||||
'Monitoring',
|
||||
'Logging'
|
||||
],
|
||||
validate=lambda choices: len(choices) > 0 or "You must select at least one feature"
|
||||
).ask()
|
||||
|
||||
# Checkbox with default selections
|
||||
dev_tools = questionary.checkbox(
|
||||
"Select development tools:",
|
||||
choices=[
|
||||
Choice('ESLint (Linting)', value='eslint', checked=True),
|
||||
Choice('Prettier (Formatting)', value='prettier', checked=True),
|
||||
Choice('Jest (Testing)', value='jest'),
|
||||
Choice('Husky (Git Hooks)', value='husky'),
|
||||
Choice('TypeDoc (Documentation)', value='typedoc'),
|
||||
Choice('Webpack (Bundling)', value='webpack')
|
||||
]
|
||||
).ask()
|
||||
|
||||
# Checkbox with separators and checked defaults
|
||||
plugins = questionary.checkbox(
|
||||
"Select plugins to install:",
|
||||
choices=[
|
||||
Separator('=== Essential ==='),
|
||||
Choice('dotenv - Environment variables', value='dotenv', checked=True),
|
||||
Choice('axios - HTTP client', value='axios', checked=True),
|
||||
Separator('=== Utilities ==='),
|
||||
Choice('lodash - Utility functions', value='lodash'),
|
||||
Choice('dayjs - Date manipulation', value='dayjs'),
|
||||
Choice('uuid - Unique IDs', value='uuid'),
|
||||
Separator('=== Validation ==='),
|
||||
Choice('joi - Schema validation', value='joi'),
|
||||
Choice('zod - TypeScript-first validation', value='zod'),
|
||||
Separator('=== Advanced ==='),
|
||||
Choice('bull - Job queues', value='bull'),
|
||||
Choice('socket.io - WebSockets', value='socket.io')
|
||||
],
|
||||
validate=MaximumChoicesValidator(maximum=10)
|
||||
).ask()
|
||||
|
||||
# Checkbox with emojis
|
||||
permissions = questionary.checkbox(
|
||||
"Grant the following permissions:",
|
||||
choices=[
|
||||
Choice('📁 Read files', value='read', checked=True),
|
||||
Choice('✏️ Write files', value='write'),
|
||||
Choice('🗑️ Delete files', value='delete'),
|
||||
Choice('🌐 Network access', value='network', checked=True),
|
||||
Choice('🖥️ System commands', value='system'),
|
||||
Choice('🔒 Keychain access', value='keychain')
|
||||
]
|
||||
).ask()
|
||||
|
||||
# Validate permissions logic
|
||||
if 'delete' in permissions and 'write' not in permissions:
|
||||
print("\n⚠️ Warning: Delete permission requires write permission")
|
||||
permissions.append('write')
|
||||
|
||||
# Checkbox with validation
|
||||
environments = questionary.checkbox(
|
||||
"Select deployment environments:",
|
||||
choices=[
|
||||
Choice('Development', value='dev', checked=True),
|
||||
Choice('Staging', value='staging'),
|
||||
Choice('Production', value='prod'),
|
||||
Choice('Testing', value='test', checked=True)
|
||||
],
|
||||
validate=lambda choices: (
|
||||
'dev' in choices or "Development environment is required"
|
||||
)
|
||||
).ask()
|
||||
|
||||
# Additional validation
|
||||
if 'prod' in environments and 'staging' not in environments:
|
||||
print("\n⚠️ Warning: Staging environment is recommended before production")
|
||||
|
||||
answers = {
|
||||
'features': features,
|
||||
'devTools': dev_tools,
|
||||
'plugins': plugins,
|
||||
'permissions': permissions,
|
||||
'environments': environments
|
||||
}
|
||||
|
||||
print("\n✅ Selected options:")
|
||||
import json
|
||||
print(json.dumps(answers, indent=2))
|
||||
|
||||
# Example: Process selections
|
||||
print("\n📦 Installing selected features...")
|
||||
for feature in features:
|
||||
print(f" - {feature}")
|
||||
|
||||
return answers
|
||||
|
||||
|
||||
def grouped_checkbox_example():
|
||||
"""Example with logically grouped checkboxes"""
|
||||
|
||||
print("\n📂 Grouped Checkbox Example\n")
|
||||
|
||||
security_features = questionary.checkbox(
|
||||
"Select security features:",
|
||||
choices=[
|
||||
Separator('=== Authentication ==='),
|
||||
Choice('JWT Tokens', value='jwt', checked=True),
|
||||
Choice('OAuth 2.0', value='oauth'),
|
||||
Choice('Session Management', value='session'),
|
||||
Choice('Two-Factor Auth', value='2fa'),
|
||||
Separator('=== Authorization ==='),
|
||||
Choice('Role-Based Access Control', value='rbac', checked=True),
|
||||
Choice('Permission System', value='permissions', checked=True),
|
||||
Choice('API Key Management', value='api-keys'),
|
||||
Separator('=== Security ==='),
|
||||
Choice('Rate Limiting', value='rate-limit', checked=True),
|
||||
Choice('CORS Configuration', value='cors', checked=True),
|
||||
Choice('Input Sanitization', value='sanitization', checked=True),
|
||||
Choice('SQL Injection Prevention', value='sql-prevent', checked=True),
|
||||
Choice('XSS Protection', value='xss-protect', checked=True),
|
||||
Separator('=== Encryption ==='),
|
||||
Choice('Data Encryption at Rest', value='encrypt-rest'),
|
||||
Choice('SSL/TLS', value='ssl', checked=True),
|
||||
Choice('Password Hashing', value='hash', checked=True)
|
||||
],
|
||||
validate=MinimumChoicesValidator(minimum=3, message="Select at least 3 security features")
|
||||
).ask()
|
||||
|
||||
print(f"\n✅ Selected {len(security_features)} security features")
|
||||
return {'securityFeatures': security_features}
|
||||
|
||||
|
||||
def dependent_checkbox_example():
|
||||
"""Example with checkboxes that depend on previous selections"""
|
||||
|
||||
print("\n🔗 Dependent Checkbox Example\n")
|
||||
|
||||
# First checkbox: Select cloud providers
|
||||
cloud_providers = questionary.checkbox(
|
||||
"Select cloud providers:",
|
||||
choices=[
|
||||
Choice('☁️ AWS', value='aws'),
|
||||
Choice('☁️ Google Cloud', value='gcp'),
|
||||
Choice('☁️ Azure', value='azure'),
|
||||
Choice('☁️ DigitalOcean', value='do')
|
||||
],
|
||||
validate=lambda c: len(c) > 0 or "Select at least one cloud provider"
|
||||
).ask()
|
||||
|
||||
# Second checkbox: AWS services (only if AWS selected)
|
||||
aws_services = []
|
||||
if 'aws' in cloud_providers:
|
||||
aws_services = questionary.checkbox(
|
||||
"Select AWS services:",
|
||||
choices=[
|
||||
Choice('EC2 - Virtual Servers', value='ec2'),
|
||||
Choice('Lambda - Serverless', value='lambda'),
|
||||
Choice('S3 - Object Storage', value='s3', checked=True),
|
||||
Choice('RDS - Databases', value='rds'),
|
||||
Choice('CloudFront - CDN', value='cloudfront')
|
||||
]
|
||||
).ask()
|
||||
|
||||
# Third checkbox: GCP services (only if GCP selected)
|
||||
gcp_services = []
|
||||
if 'gcp' in cloud_providers:
|
||||
gcp_services = questionary.checkbox(
|
||||
"Select GCP services:",
|
||||
choices=[
|
||||
Choice('Compute Engine', value='compute'),
|
||||
Choice('Cloud Functions', value='functions'),
|
||||
Choice('Cloud Storage', value='storage', checked=True),
|
||||
Choice('Cloud SQL', value='sql'),
|
||||
Choice('Cloud CDN', value='cdn')
|
||||
]
|
||||
).ask()
|
||||
|
||||
result = {
|
||||
'cloudProviders': cloud_providers,
|
||||
'awsServices': aws_services,
|
||||
'gcpServices': gcp_services
|
||||
}
|
||||
|
||||
print("\n✅ Configuration complete:")
|
||||
import json
|
||||
print(json.dumps(result, indent=2))
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def conditional_validation_example():
|
||||
"""Example with conditional validation logic"""
|
||||
|
||||
print("\n🔍 Conditional Validation Example\n")
|
||||
|
||||
database_features = questionary.checkbox(
|
||||
"Select database features:",
|
||||
choices=[
|
||||
Choice('Connection Pooling', value='pool', checked=True),
|
||||
Choice('Migrations', value='migrations', checked=True),
|
||||
Choice('Transactions', value='transactions'),
|
||||
Choice('Replication', value='replication'),
|
||||
Choice('Sharding', value='sharding'),
|
||||
Choice('Caching', value='caching')
|
||||
]
|
||||
).ask()
|
||||
|
||||
# Conditional logic: Sharding requires replication
|
||||
if 'sharding' in database_features and 'replication' not in database_features:
|
||||
print("\n⚠️ Sharding requires replication. Adding replication...")
|
||||
database_features.append('replication')
|
||||
|
||||
# Conditional logic: Caching works best with pooling
|
||||
if 'caching' in database_features and 'pool' not in database_features:
|
||||
add_pooling = questionary.confirm(
|
||||
"Caching works best with connection pooling. Add it?",
|
||||
default=True
|
||||
).ask()
|
||||
if add_pooling:
|
||||
database_features.append('pool')
|
||||
|
||||
print(f"\n✅ Selected {len(database_features)} database features")
|
||||
return {'databaseFeatures': database_features}
|
||||
|
||||
|
||||
def main():
|
||||
"""Run checkbox prompt examples"""
|
||||
try:
|
||||
print("=== Checkbox Prompt Examples ===")
|
||||
|
||||
# Example 1: Basic checkbox selections
|
||||
checkbox_prompt_example()
|
||||
|
||||
# Example 2: Grouped checkboxes
|
||||
grouped_checkbox_example()
|
||||
|
||||
# Example 3: Dependent checkboxes
|
||||
dependent_checkbox_example()
|
||||
|
||||
# Example 4: Conditional validation
|
||||
conditional_validation_example()
|
||||
|
||||
print("\n✅ Checkbox examples complete!")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n❌ User interrupted prompt")
|
||||
exit(1)
|
||||
except Exception as e:
|
||||
print(f"\n\n❌ Error: {e}")
|
||||
exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
501
skills/inquirer-patterns/templates/python/conditional_prompt.py
Normal file
501
skills/inquirer-patterns/templates/python/conditional_prompt.py
Normal file
@@ -0,0 +1,501 @@
|
||||
"""
|
||||
Conditional Prompt Template
|
||||
|
||||
Use for: Dynamic forms based on previous answers
|
||||
Features: Skip logic, dependent questions, branching
|
||||
"""
|
||||
|
||||
import questionary
|
||||
from questionary import Choice, Separator
|
||||
|
||||
|
||||
def conditional_prompt_example():
|
||||
"""Example conditional prompts"""
|
||||
|
||||
print("\n🔀 Conditional Prompt Example\n")
|
||||
|
||||
# First question: Use database?
|
||||
use_database = questionary.confirm(
|
||||
"Do you want to use a database?",
|
||||
default=True
|
||||
).ask()
|
||||
|
||||
database_config = {}
|
||||
|
||||
if use_database:
|
||||
# Database type
|
||||
database_type = questionary.select(
|
||||
"Select database type:",
|
||||
choices=['PostgreSQL', 'MySQL', 'MongoDB', 'SQLite', 'Redis']
|
||||
).ask()
|
||||
|
||||
database_config['databaseType'] = database_type
|
||||
|
||||
# Host and port (not for SQLite)
|
||||
if database_type != 'SQLite':
|
||||
database_host = questionary.text(
|
||||
"Database host:",
|
||||
default="localhost"
|
||||
).ask()
|
||||
|
||||
# Default ports based on database type
|
||||
default_ports = {
|
||||
'PostgreSQL': '5432',
|
||||
'MySQL': '3306',
|
||||
'MongoDB': '27017',
|
||||
'Redis': '6379'
|
||||
}
|
||||
|
||||
database_port = questionary.text(
|
||||
"Database port:",
|
||||
default=default_ports.get(database_type, '5432')
|
||||
).ask()
|
||||
|
||||
database_config['host'] = database_host
|
||||
database_config['port'] = database_port
|
||||
|
||||
# Database name
|
||||
database_name = questionary.text(
|
||||
"Database name:",
|
||||
validate=lambda text: len(text) > 0 or "Database name required"
|
||||
).ask()
|
||||
|
||||
database_config['databaseName'] = database_name
|
||||
|
||||
# Authentication
|
||||
use_authentication = questionary.confirm(
|
||||
"Do you want to use authentication?",
|
||||
default=True
|
||||
).ask()
|
||||
|
||||
if use_authentication:
|
||||
database_username = questionary.text(
|
||||
"Database username:",
|
||||
validate=lambda text: len(text) > 0 or "Username required"
|
||||
).ask()
|
||||
|
||||
database_password = questionary.password(
|
||||
"Database password:"
|
||||
).ask()
|
||||
|
||||
database_config['useAuthentication'] = True
|
||||
database_config['username'] = database_username
|
||||
# Don't store actual password in answers
|
||||
|
||||
# SSL (only for remote hosts)
|
||||
if database_type != 'SQLite' and database_config.get('host') != 'localhost':
|
||||
use_ssl = questionary.confirm(
|
||||
"Use SSL connection?",
|
||||
default=False
|
||||
).ask()
|
||||
|
||||
if use_ssl:
|
||||
ssl_cert_path = questionary.text(
|
||||
"Path to SSL certificate:",
|
||||
validate=lambda text: len(text) > 0 or "SSL certificate path required"
|
||||
).ask()
|
||||
|
||||
database_config['useSSL'] = True
|
||||
database_config['sslCertPath'] = ssl_cert_path
|
||||
|
||||
answers = {
|
||||
'useDatabase': use_database,
|
||||
'databaseConfig': database_config if use_database else None
|
||||
}
|
||||
|
||||
print("\n✅ Configuration:")
|
||||
import json
|
||||
print(json.dumps(answers, indent=2))
|
||||
|
||||
return answers
|
||||
|
||||
|
||||
def deployment_wizard():
|
||||
"""Example: Deployment configuration wizard"""
|
||||
|
||||
print("\n🚀 Deployment Configuration Wizard\n")
|
||||
|
||||
# Environment
|
||||
environment = questionary.select(
|
||||
"Select deployment environment:",
|
||||
choices=['Development', 'Staging', 'Production']
|
||||
).ask()
|
||||
|
||||
config = {'environment': environment}
|
||||
|
||||
# Docker
|
||||
use_docker = questionary.confirm(
|
||||
"Deploy using Docker?",
|
||||
default=True
|
||||
).ask()
|
||||
|
||||
config['useDocker'] = use_docker
|
||||
|
||||
if use_docker:
|
||||
docker_image = questionary.text(
|
||||
"Docker image name:",
|
||||
default="myapp:latest",
|
||||
validate=lambda text: len(text) > 0 or "Docker image name required"
|
||||
).ask()
|
||||
|
||||
config['dockerImage'] = docker_image
|
||||
|
||||
registry = questionary.select(
|
||||
"Container registry:",
|
||||
choices=[
|
||||
'Docker Hub',
|
||||
'GitHub Container Registry',
|
||||
'AWS ECR',
|
||||
'Google Artifact Registry'
|
||||
]
|
||||
).ask()
|
||||
|
||||
config['registry'] = registry
|
||||
|
||||
# Platform
|
||||
platform = questionary.select(
|
||||
"Deployment platform:",
|
||||
choices=[
|
||||
'AWS', 'Google Cloud', 'Azure',
|
||||
'DigitalOcean', 'Vercel', 'Netlify', 'Self-hosted'
|
||||
]
|
||||
).ask()
|
||||
|
||||
config['platform'] = platform
|
||||
|
||||
# Platform-specific configuration
|
||||
if platform == 'AWS':
|
||||
aws_service = questionary.select(
|
||||
"AWS service:",
|
||||
choices=['ECS', 'EKS', 'Lambda', 'Elastic Beanstalk', 'EC2']
|
||||
).ask()
|
||||
config['awsService'] = aws_service
|
||||
|
||||
# Auto-scaling (only for certain services)
|
||||
if aws_service in ['ECS', 'EKS']:
|
||||
auto_scale = questionary.confirm(
|
||||
"Enable auto-scaling?",
|
||||
default=True
|
||||
).ask()
|
||||
|
||||
config['autoScale'] = auto_scale
|
||||
|
||||
if auto_scale:
|
||||
min_instances = questionary.text(
|
||||
"Minimum instances:",
|
||||
default="1",
|
||||
validate=lambda text: text.isdigit() and int(text) > 0 or "Must be at least 1"
|
||||
).ask()
|
||||
|
||||
max_instances = questionary.text(
|
||||
"Maximum instances:",
|
||||
default="10",
|
||||
validate=lambda text: text.isdigit() and int(text) >= int(min_instances) or f"Must be at least {min_instances}"
|
||||
).ask()
|
||||
|
||||
config['minInstances'] = int(min_instances)
|
||||
config['maxInstances'] = int(max_instances)
|
||||
|
||||
elif platform == 'Google Cloud':
|
||||
gcp_service = questionary.select(
|
||||
"Google Cloud service:",
|
||||
choices=['Cloud Run', 'GKE', 'App Engine', 'Compute Engine']
|
||||
).ask()
|
||||
config['gcpService'] = gcp_service
|
||||
|
||||
# CDN (only for production)
|
||||
if environment == 'Production':
|
||||
use_cdn = questionary.confirm(
|
||||
"Use CDN for static assets?",
|
||||
default=True
|
||||
).ask()
|
||||
|
||||
config['useCDN'] = use_cdn
|
||||
|
||||
if use_cdn:
|
||||
cdn_provider = questionary.select(
|
||||
"CDN provider:",
|
||||
choices=['CloudFlare', 'AWS CloudFront', 'Google Cloud CDN', 'Azure CDN']
|
||||
).ask()
|
||||
config['cdnProvider'] = cdn_provider
|
||||
|
||||
# Monitoring
|
||||
setup_monitoring = questionary.confirm(
|
||||
"Setup monitoring?",
|
||||
default=True
|
||||
).ask()
|
||||
|
||||
if setup_monitoring:
|
||||
monitoring_tools = questionary.checkbox(
|
||||
"Select monitoring tools:",
|
||||
choices=['Prometheus', 'Grafana', 'Datadog', 'New Relic', 'Sentry'],
|
||||
validate=lambda choices: len(choices) > 0 or "Select at least one tool"
|
||||
).ask()
|
||||
|
||||
config['monitoringTools'] = monitoring_tools
|
||||
|
||||
print("\n✅ Deployment configuration complete!")
|
||||
import json
|
||||
print(json.dumps(config, indent=2))
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def feature_flag_wizard():
|
||||
"""Example: Feature flag configuration"""
|
||||
|
||||
print("\n🎛️ Feature Flag Configuration\n")
|
||||
|
||||
# Feature name
|
||||
feature_name = questionary.text(
|
||||
"Feature name:",
|
||||
validate=lambda text: text and text.replace('-', '').replace('_', '').islower() or "Use lowercase, hyphens, underscores only"
|
||||
).ask()
|
||||
|
||||
# Enabled by default
|
||||
enabled_by_default = questionary.confirm(
|
||||
"Enabled by default?",
|
||||
default=False
|
||||
).ask()
|
||||
|
||||
config = {
|
||||
'featureName': feature_name,
|
||||
'enabledByDefault': enabled_by_default
|
||||
}
|
||||
|
||||
# Rollout strategy
|
||||
rollout_strategy = questionary.select(
|
||||
"Rollout strategy:",
|
||||
choices=[
|
||||
'All users',
|
||||
'Percentage rollout',
|
||||
'User targeting',
|
||||
'Beta users only',
|
||||
'Manual control'
|
||||
]
|
||||
).ask()
|
||||
|
||||
config['rolloutStrategy'] = rollout_strategy
|
||||
|
||||
# Percentage rollout
|
||||
if rollout_strategy == 'Percentage rollout':
|
||||
rollout_percentage = questionary.text(
|
||||
"Rollout percentage (0-100):",
|
||||
default="10",
|
||||
validate=lambda text: text.isdigit() and 0 <= int(text) <= 100 or "Must be between 0 and 100"
|
||||
).ask()
|
||||
|
||||
config['rolloutPercentage'] = int(rollout_percentage)
|
||||
|
||||
# User targeting
|
||||
if rollout_strategy == 'User targeting':
|
||||
target_user_groups = questionary.checkbox(
|
||||
"Target user groups:",
|
||||
choices=[
|
||||
'Beta testers',
|
||||
'Premium users',
|
||||
'Internal team',
|
||||
'Early adopters',
|
||||
'Specific regions'
|
||||
],
|
||||
validate=lambda choices: len(choices) > 0 or "Select at least one group"
|
||||
).ask()
|
||||
|
||||
config['targetUserGroups'] = target_user_groups
|
||||
|
||||
# Specific regions
|
||||
if 'Specific regions' in target_user_groups:
|
||||
target_regions = questionary.checkbox(
|
||||
"Target regions:",
|
||||
choices=[
|
||||
'North America',
|
||||
'Europe',
|
||||
'Asia Pacific',
|
||||
'South America',
|
||||
'Africa'
|
||||
]
|
||||
).ask()
|
||||
|
||||
config['targetRegions'] = target_regions
|
||||
|
||||
# Metrics
|
||||
enable_metrics = questionary.confirm(
|
||||
"Track feature usage metrics?",
|
||||
default=True
|
||||
).ask()
|
||||
|
||||
if enable_metrics:
|
||||
metrics = questionary.checkbox(
|
||||
"Select metrics to track:",
|
||||
choices=[
|
||||
'Usage count',
|
||||
'User adoption rate',
|
||||
'Performance impact',
|
||||
'Error rate',
|
||||
'User feedback'
|
||||
]
|
||||
).ask()
|
||||
|
||||
config['metrics'] = metrics
|
||||
|
||||
# Expiration
|
||||
add_expiration_date = questionary.confirm(
|
||||
"Set feature flag expiration?",
|
||||
default=False
|
||||
).ask()
|
||||
|
||||
if add_expiration_date:
|
||||
expiration_date = questionary.text(
|
||||
"Expiration date (YYYY-MM-DD):",
|
||||
validate=lambda text: len(text) == 10 and text.count('-') == 2 or "Use format YYYY-MM-DD"
|
||||
).ask()
|
||||
|
||||
config['expirationDate'] = expiration_date
|
||||
|
||||
print("\n✅ Feature flag configured!")
|
||||
import json
|
||||
print(json.dumps(config, indent=2))
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def cicd_pipeline_wizard():
|
||||
"""Example: CI/CD pipeline setup"""
|
||||
|
||||
print("\n⚙️ CI/CD Pipeline Configuration\n")
|
||||
|
||||
# Provider
|
||||
provider = questionary.select(
|
||||
"CI/CD provider:",
|
||||
choices=['GitHub Actions', 'GitLab CI', 'CircleCI', 'Jenkins', 'Travis CI']
|
||||
).ask()
|
||||
|
||||
config = {'provider': provider}
|
||||
|
||||
# Triggers
|
||||
triggers = questionary.checkbox(
|
||||
"Pipeline triggers:",
|
||||
choices=[
|
||||
'Push to main/master',
|
||||
'Pull request',
|
||||
'Tag creation',
|
||||
'Manual trigger',
|
||||
'Scheduled (cron)'
|
||||
],
|
||||
default=['Push to main/master', 'Pull request']
|
||||
).ask()
|
||||
|
||||
config['triggers'] = triggers
|
||||
|
||||
# Cron schedule
|
||||
if 'Scheduled (cron)' in triggers:
|
||||
cron_schedule = questionary.text(
|
||||
"Cron schedule:",
|
||||
default="0 2 * * *",
|
||||
validate=lambda text: len(text.split()) == 5 or "Invalid cron format (5 parts required)"
|
||||
).ask()
|
||||
|
||||
config['cronSchedule'] = cron_schedule
|
||||
|
||||
# Stages
|
||||
stages = questionary.checkbox(
|
||||
"Pipeline stages:",
|
||||
choices=['Build', 'Test', 'Lint', 'Security scan', 'Deploy'],
|
||||
default=['Build', 'Test', 'Deploy'],
|
||||
validate=lambda choices: len(choices) > 0 or "Select at least one stage"
|
||||
).ask()
|
||||
|
||||
config['stages'] = stages
|
||||
|
||||
# Test types
|
||||
if 'Test' in stages:
|
||||
test_types = questionary.checkbox(
|
||||
"Test types to run:",
|
||||
choices=[
|
||||
'Unit tests',
|
||||
'Integration tests',
|
||||
'E2E tests',
|
||||
'Performance tests'
|
||||
]
|
||||
).ask()
|
||||
|
||||
config['testTypes'] = test_types
|
||||
|
||||
# Security tools
|
||||
if 'Security scan' in stages:
|
||||
security_tools = questionary.checkbox(
|
||||
"Security scanning tools:",
|
||||
choices=['Snyk', 'Dependabot', 'SonarQube', 'OWASP Dependency Check']
|
||||
).ask()
|
||||
|
||||
config['securityTools'] = security_tools
|
||||
|
||||
# Deploy environments
|
||||
if 'Deploy' in stages:
|
||||
deploy_environments = questionary.checkbox(
|
||||
"Deployment environments:",
|
||||
choices=['Development', 'Staging', 'Production'],
|
||||
default=['Staging', 'Production']
|
||||
).ask()
|
||||
|
||||
config['deployEnvironments'] = deploy_environments
|
||||
|
||||
# Approval for production
|
||||
if 'Production' in deploy_environments:
|
||||
require_approval = questionary.confirm(
|
||||
"Require manual approval for production?",
|
||||
default=True
|
||||
).ask()
|
||||
|
||||
config['requireApproval'] = require_approval
|
||||
|
||||
# Notifications
|
||||
enable_notifications = questionary.confirm(
|
||||
"Enable build notifications?",
|
||||
default=True
|
||||
).ask()
|
||||
|
||||
if enable_notifications:
|
||||
notification_channels = questionary.checkbox(
|
||||
"Notification channels:",
|
||||
choices=['Email', 'Slack', 'Discord', 'Microsoft Teams']
|
||||
).ask()
|
||||
|
||||
config['notificationChannels'] = notification_channels
|
||||
|
||||
print("\n✅ CI/CD pipeline configured!")
|
||||
import json
|
||||
print(json.dumps(config, indent=2))
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def main():
|
||||
"""Run conditional prompt examples"""
|
||||
try:
|
||||
print("=== Conditional Prompt Examples ===")
|
||||
|
||||
# Example 1: Database configuration
|
||||
conditional_prompt_example()
|
||||
|
||||
# Example 2: Deployment wizard
|
||||
deployment_wizard()
|
||||
|
||||
# Example 3: Feature flag configuration
|
||||
feature_flag_wizard()
|
||||
|
||||
# Example 4: CI/CD pipeline setup
|
||||
cicd_pipeline_wizard()
|
||||
|
||||
print("\n✅ Conditional prompt examples complete!")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n❌ User interrupted prompt")
|
||||
exit(1)
|
||||
except Exception as e:
|
||||
print(f"\n\n❌ Error: {e}")
|
||||
exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
221
skills/inquirer-patterns/templates/python/list_prompt.py
Normal file
221
skills/inquirer-patterns/templates/python/list_prompt.py
Normal file
@@ -0,0 +1,221 @@
|
||||
"""
|
||||
List Selection Prompt Template
|
||||
|
||||
Use for: Single choice from predefined options
|
||||
Features: Arrow key navigation, search filtering
|
||||
"""
|
||||
|
||||
import questionary
|
||||
from questionary import Choice, Separator
|
||||
|
||||
|
||||
def list_prompt_example():
|
||||
"""Example list selection prompts"""
|
||||
|
||||
print("\n📋 List Selection Example\n")
|
||||
|
||||
# Simple list
|
||||
framework = questionary.select(
|
||||
"Choose your preferred framework:",
|
||||
choices=[
|
||||
'React',
|
||||
'Vue',
|
||||
'Angular',
|
||||
'Svelte',
|
||||
'Next.js',
|
||||
'Nuxt.js'
|
||||
],
|
||||
default='React'
|
||||
).ask()
|
||||
|
||||
# List with values
|
||||
language = questionary.select(
|
||||
"Choose programming language:",
|
||||
choices=[
|
||||
Choice('JavaScript', value='js'),
|
||||
Choice('TypeScript', value='ts'),
|
||||
Choice('Python', value='py'),
|
||||
Choice('Ruby', value='rb'),
|
||||
Choice('Go', value='go')
|
||||
],
|
||||
default='ts'
|
||||
).ask()
|
||||
|
||||
# List with descriptions
|
||||
package_manager = questionary.select(
|
||||
"Choose package manager:",
|
||||
choices=[
|
||||
Choice('npm - Node Package Manager', value='npm', shortcut_key='n'),
|
||||
Choice('yarn - Fast, reliable package manager', value='yarn', shortcut_key='y'),
|
||||
Choice('pnpm - Fast, disk space efficient', value='pnpm', shortcut_key='p'),
|
||||
Choice('bun - All-in-one toolkit', value='bun', shortcut_key='b')
|
||||
]
|
||||
).ask()
|
||||
|
||||
# List with separators
|
||||
environment = questionary.select(
|
||||
"Select deployment environment:",
|
||||
choices=[
|
||||
Separator('--- Cloud Platforms ---'),
|
||||
'AWS',
|
||||
'Google Cloud',
|
||||
'Azure',
|
||||
Separator('--- Serverless ---'),
|
||||
'Vercel',
|
||||
'Netlify',
|
||||
'Cloudflare Workers',
|
||||
Separator('--- Self-hosted ---'),
|
||||
'Docker',
|
||||
'Kubernetes'
|
||||
]
|
||||
).ask()
|
||||
|
||||
# List with emojis and styling
|
||||
database = questionary.select(
|
||||
"Choose database:",
|
||||
choices=[
|
||||
Choice('🐘 PostgreSQL (Relational)', value='postgresql'),
|
||||
Choice('🐬 MySQL (Relational)', value='mysql'),
|
||||
Choice('🍃 MongoDB (Document)', value='mongodb'),
|
||||
Choice('⚡ Redis (Key-Value)', value='redis'),
|
||||
Choice('📊 SQLite (Embedded)', value='sqlite'),
|
||||
Choice('🔥 Supabase (PostgreSQL + APIs)', value='supabase')
|
||||
],
|
||||
use_shortcuts=True,
|
||||
use_arrow_keys=True
|
||||
).ask()
|
||||
|
||||
answers = {
|
||||
'framework': framework,
|
||||
'language': language,
|
||||
'packageManager': package_manager,
|
||||
'environment': environment,
|
||||
'database': database
|
||||
}
|
||||
|
||||
print("\n✅ Selections:")
|
||||
import json
|
||||
print(json.dumps(answers, indent=2))
|
||||
|
||||
return answers
|
||||
|
||||
|
||||
def dynamic_list_example():
|
||||
"""Example with dynamic choices based on context"""
|
||||
|
||||
print("\n🔄 Dynamic List Example\n")
|
||||
|
||||
# First selection
|
||||
project_type = questionary.select(
|
||||
"Project type:",
|
||||
choices=['Web Application', 'CLI Tool', 'API/Backend', 'Library']
|
||||
).ask()
|
||||
|
||||
# Dynamic framework choices based on project type
|
||||
framework_choices = {
|
||||
'Web Application': ['React', 'Vue', 'Angular', 'Svelte', 'Next.js'],
|
||||
'CLI Tool': ['Commander.js', 'Yargs', 'Click', 'Typer', 'Cobra'],
|
||||
'API/Backend': ['Express', 'Fastify', 'Flask', 'FastAPI', 'Gin'],
|
||||
'Library': ['TypeScript', 'JavaScript', 'Python', 'Go', 'Rust']
|
||||
}
|
||||
|
||||
framework = questionary.select(
|
||||
f"Choose framework for {project_type}:",
|
||||
choices=framework_choices.get(project_type, ['None'])
|
||||
).ask()
|
||||
|
||||
print(f"\n✅ Selected: {project_type} with {framework}")
|
||||
|
||||
return {'projectType': project_type, 'framework': framework}
|
||||
|
||||
|
||||
def categorized_list_example():
|
||||
"""Example with categorized options"""
|
||||
|
||||
print("\n📂 Categorized List Example\n")
|
||||
|
||||
cloud_service = questionary.select(
|
||||
"Choose cloud service:",
|
||||
choices=[
|
||||
Separator('=== Compute ==='),
|
||||
Choice('EC2 - Virtual Servers', value='ec2'),
|
||||
Choice('Lambda - Serverless Functions', value='lambda'),
|
||||
Choice('ECS - Container Service', value='ecs'),
|
||||
Choice('EKS - Kubernetes Service', value='eks'),
|
||||
Separator('=== Storage ==='),
|
||||
Choice('S3 - Object Storage', value='s3'),
|
||||
Choice('EBS - Block Storage', value='ebs'),
|
||||
Choice('EFS - File System', value='efs'),
|
||||
Separator('=== Database ==='),
|
||||
Choice('RDS - Relational Database', value='rds'),
|
||||
Choice('DynamoDB - NoSQL Database', value='dynamodb'),
|
||||
Choice('ElastiCache - In-Memory Cache', value='elasticache'),
|
||||
Separator('=== Other ==='),
|
||||
Choice('CloudFront - CDN', value='cloudfront'),
|
||||
Choice('Route53 - DNS', value='route53'),
|
||||
Choice('SQS - Message Queue', value='sqs')
|
||||
],
|
||||
use_indicator=True
|
||||
).ask()
|
||||
|
||||
print(f"\n✅ Selected: {cloud_service}")
|
||||
|
||||
return {'cloudService': cloud_service}
|
||||
|
||||
|
||||
def numbered_list_example():
|
||||
"""Example with numbered choices for easier selection"""
|
||||
|
||||
print("\n🔢 Numbered List Example\n")
|
||||
|
||||
languages = [
|
||||
'Python', 'JavaScript', 'TypeScript', 'Go', 'Rust',
|
||||
'Java', 'C++', 'Ruby', 'PHP', 'Swift'
|
||||
]
|
||||
|
||||
# Add numbers to choices for easier reference
|
||||
numbered_choices = [
|
||||
Choice(f"{i+1}. {lang}", value=lang)
|
||||
for i, lang in enumerate(languages)
|
||||
]
|
||||
|
||||
language = questionary.select(
|
||||
"Choose programming language:",
|
||||
choices=numbered_choices,
|
||||
use_shortcuts=False # Disable letter shortcuts when using numbers
|
||||
).ask()
|
||||
|
||||
print(f"\n✅ Selected: {language}")
|
||||
|
||||
return {'language': language}
|
||||
|
||||
|
||||
def main():
|
||||
"""Run list prompt examples"""
|
||||
try:
|
||||
print("=== List Selection Examples ===")
|
||||
|
||||
# Example 1: Basic list selections
|
||||
list_prompt_example()
|
||||
|
||||
# Example 2: Dynamic choices
|
||||
dynamic_list_example()
|
||||
|
||||
# Example 3: Categorized options
|
||||
categorized_list_example()
|
||||
|
||||
# Example 4: Numbered list
|
||||
numbered_list_example()
|
||||
|
||||
print("\n✅ List selection examples complete!")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n❌ User interrupted prompt")
|
||||
exit(1)
|
||||
except Exception as e:
|
||||
print(f"\n\n❌ Error: {e}")
|
||||
exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
366
skills/inquirer-patterns/templates/python/password_prompt.py
Normal file
366
skills/inquirer-patterns/templates/python/password_prompt.py
Normal file
@@ -0,0 +1,366 @@
|
||||
"""
|
||||
Password Prompt Template
|
||||
|
||||
Use for: Sensitive input (credentials, tokens)
|
||||
Features: Hidden input, confirmation, validation
|
||||
"""
|
||||
|
||||
import questionary
|
||||
import re
|
||||
from questionary import ValidationError, Validator
|
||||
|
||||
|
||||
class PasswordStrengthValidator(Validator):
|
||||
"""Validator for password strength requirements"""
|
||||
|
||||
def __init__(self, min_length=8, require_uppercase=True, require_lowercase=True,
|
||||
require_digit=True, require_special=True):
|
||||
self.min_length = min_length
|
||||
self.require_uppercase = require_uppercase
|
||||
self.require_lowercase = require_lowercase
|
||||
self.require_digit = require_digit
|
||||
self.require_special = require_special
|
||||
|
||||
def validate(self, document):
|
||||
password = document.text
|
||||
issues = []
|
||||
|
||||
if len(password) < self.min_length:
|
||||
issues.append(f"at least {self.min_length} characters")
|
||||
|
||||
if self.require_uppercase and not re.search(r'[A-Z]', password):
|
||||
issues.append("an uppercase letter")
|
||||
|
||||
if self.require_lowercase and not re.search(r'[a-z]', password):
|
||||
issues.append("a lowercase letter")
|
||||
|
||||
if self.require_digit and not re.search(r'[0-9]', password):
|
||||
issues.append("a number")
|
||||
|
||||
if self.require_special and not re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
|
||||
issues.append("a special character")
|
||||
|
||||
if issues:
|
||||
raise ValidationError(
|
||||
message=f"Password must contain: {', '.join(issues)}",
|
||||
cursor_position=len(password)
|
||||
)
|
||||
|
||||
|
||||
class APIKeyValidator(Validator):
|
||||
"""Validator for API key format"""
|
||||
|
||||
def validate(self, document):
|
||||
api_key = document.text
|
||||
|
||||
if len(api_key) == 0:
|
||||
raise ValidationError(
|
||||
message="API key is required",
|
||||
cursor_position=0
|
||||
)
|
||||
|
||||
if not (api_key.startswith('sk-') or api_key.startswith('pk-')):
|
||||
raise ValidationError(
|
||||
message='API key must start with "sk-" or "pk-"',
|
||||
cursor_position=len(api_key)
|
||||
)
|
||||
|
||||
if len(api_key) < 32:
|
||||
raise ValidationError(
|
||||
message="API key appears to be too short",
|
||||
cursor_position=len(api_key)
|
||||
)
|
||||
|
||||
|
||||
def calculate_password_strength(password):
|
||||
"""Calculate password strength score"""
|
||||
strength = 0
|
||||
|
||||
if len(password) >= 8:
|
||||
strength += 1
|
||||
if len(password) >= 12:
|
||||
strength += 1
|
||||
if re.search(r'[a-z]', password):
|
||||
strength += 1
|
||||
if re.search(r'[A-Z]', password):
|
||||
strength += 1
|
||||
if re.search(r'[0-9]', password):
|
||||
strength += 1
|
||||
if re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
|
||||
strength += 1
|
||||
|
||||
levels = ['Very Weak', 'Weak', 'Fair', 'Good', 'Strong', 'Very Strong']
|
||||
return levels[min(strength, len(levels) - 1)]
|
||||
|
||||
|
||||
def password_prompt_example():
|
||||
"""Example password prompts with validation"""
|
||||
|
||||
print("\n🔒 Password Prompt Example\n")
|
||||
|
||||
# Password with strength validation
|
||||
password = questionary.password(
|
||||
"Enter your password:",
|
||||
validate=PasswordStrengthValidator(min_length=8)
|
||||
).ask()
|
||||
|
||||
# Confirm password
|
||||
confirm_password = questionary.password(
|
||||
"Confirm your password:",
|
||||
validate=lambda text: text == password or "Passwords do not match"
|
||||
).ask()
|
||||
|
||||
print(f"\n✅ Password strength: {calculate_password_strength(password)}")
|
||||
|
||||
# API key input
|
||||
api_key = questionary.password(
|
||||
"Enter your API key:",
|
||||
validate=APIKeyValidator()
|
||||
).ask()
|
||||
|
||||
print(f"✅ API key format: {api_key[:6]}...")
|
||||
|
||||
# Optional encryption key
|
||||
encryption_key = questionary.password(
|
||||
"Enter encryption key (optional, press Enter to skip):",
|
||||
validate=lambda text: len(text) == 0 or len(text) >= 16 or "Encryption key must be at least 16 characters"
|
||||
).ask()
|
||||
|
||||
# Don't log actual passwords!
|
||||
answers = {
|
||||
'passwordSet': True,
|
||||
'passwordStrength': calculate_password_strength(password),
|
||||
'apiKeyPrefix': api_key[:6],
|
||||
'encryptionKeySet': len(encryption_key) > 0
|
||||
}
|
||||
|
||||
print("\n✅ Credentials received (not displayed for security)")
|
||||
import json
|
||||
print(json.dumps(answers, indent=2))
|
||||
|
||||
return answers
|
||||
|
||||
|
||||
def secure_account_setup():
|
||||
"""Example: Complete secure account setup"""
|
||||
|
||||
print("\n🔐 Secure Account Setup\n")
|
||||
|
||||
# Username
|
||||
username = questionary.text(
|
||||
"Username:",
|
||||
validate=lambda text: len(text) > 0 or "Username required"
|
||||
).ask()
|
||||
|
||||
# Strong password
|
||||
print("\n📝 Password requirements:")
|
||||
print(" • At least 12 characters")
|
||||
print(" • Uppercase and lowercase letters")
|
||||
print(" • Numbers")
|
||||
print(" • Special characters (!@#$%^&*)")
|
||||
print()
|
||||
|
||||
password = questionary.password(
|
||||
"Password:",
|
||||
validate=PasswordStrengthValidator(
|
||||
min_length=12,
|
||||
require_uppercase=True,
|
||||
require_lowercase=True,
|
||||
require_digit=True,
|
||||
require_special=True
|
||||
)
|
||||
).ask()
|
||||
|
||||
# Confirm password
|
||||
confirm = questionary.password(
|
||||
"Confirm password:",
|
||||
validate=lambda text: text == password or "Passwords do not match"
|
||||
).ask()
|
||||
|
||||
# Optional: Remember credentials
|
||||
remember = questionary.confirm(
|
||||
"Remember credentials? (stored securely)",
|
||||
default=False
|
||||
).ask()
|
||||
|
||||
strength = calculate_password_strength(password)
|
||||
|
||||
print(f"\n✅ Account created for: {username}")
|
||||
print(f"🔒 Password strength: {strength}")
|
||||
|
||||
if remember:
|
||||
print("💾 Credentials will be stored securely")
|
||||
|
||||
return {
|
||||
'username': username,
|
||||
'passwordStrength': strength,
|
||||
'remember': remember
|
||||
}
|
||||
|
||||
|
||||
def database_credentials_setup():
|
||||
"""Example: Database connection credentials"""
|
||||
|
||||
print("\n🗄️ Database Credentials Setup\n")
|
||||
|
||||
# Database username
|
||||
db_user = questionary.text(
|
||||
"Database username:",
|
||||
default="postgres",
|
||||
validate=lambda text: len(text) > 0 or "Username required"
|
||||
).ask()
|
||||
|
||||
# Database password
|
||||
db_password = questionary.password(
|
||||
"Database password:",
|
||||
validate=lambda text: len(text) >= 8 or "Password must be at least 8 characters"
|
||||
).ask()
|
||||
|
||||
# Admin password (if needed)
|
||||
is_admin = questionary.confirm(
|
||||
"Create admin user?",
|
||||
default=False
|
||||
).ask()
|
||||
|
||||
admin_password = None
|
||||
if is_admin:
|
||||
admin_password = questionary.password(
|
||||
"Admin password:",
|
||||
validate=PasswordStrengthValidator(min_length=12)
|
||||
).ask()
|
||||
|
||||
admin_confirm = questionary.password(
|
||||
"Confirm admin password:",
|
||||
validate=lambda text: text == admin_password or "Passwords do not match"
|
||||
).ask()
|
||||
|
||||
print(f"\n✅ Admin password strength: {calculate_password_strength(admin_password)}")
|
||||
|
||||
credentials = {
|
||||
'dbUser': db_user,
|
||||
'dbPasswordSet': True,
|
||||
'adminConfigured': is_admin
|
||||
}
|
||||
|
||||
print("\n✅ Database credentials configured")
|
||||
import json
|
||||
print(json.dumps(credentials, indent=2))
|
||||
|
||||
return credentials
|
||||
|
||||
|
||||
def api_token_setup():
|
||||
"""Example: API token and secret key setup"""
|
||||
|
||||
print("\n🔑 API Token Setup\n")
|
||||
|
||||
# API key
|
||||
api_key = questionary.password(
|
||||
"Enter API key:",
|
||||
validate=lambda text: len(text) > 0 or "API key required"
|
||||
).ask()
|
||||
|
||||
# Secret key
|
||||
secret_key = questionary.password(
|
||||
"Enter secret key:",
|
||||
validate=lambda text: len(text) >= 32 or "Secret key must be at least 32 characters"
|
||||
).ask()
|
||||
|
||||
# Webhook secret (optional)
|
||||
use_webhooks = questionary.confirm(
|
||||
"Configure webhook authentication?",
|
||||
default=False
|
||||
).ask()
|
||||
|
||||
webhook_secret = None
|
||||
if use_webhooks:
|
||||
webhook_secret = questionary.password(
|
||||
"Webhook secret:",
|
||||
validate=lambda text: len(text) >= 16 or "Webhook secret must be at least 16 characters"
|
||||
).ask()
|
||||
|
||||
# Environment
|
||||
environment = questionary.select(
|
||||
"Environment:",
|
||||
choices=['Development', 'Staging', 'Production']
|
||||
).ask()
|
||||
|
||||
config = {
|
||||
'apiKeySet': True,
|
||||
'secretKeySet': True,
|
||||
'webhookConfigured': use_webhooks,
|
||||
'environment': environment
|
||||
}
|
||||
|
||||
print("\n✅ API credentials configured")
|
||||
print(f"Environment: {environment}")
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def password_change_flow():
|
||||
"""Example: Password change with old password verification"""
|
||||
|
||||
print("\n🔄 Change Password\n")
|
||||
|
||||
# Old password (in real app, verify against stored hash)
|
||||
old_password = questionary.password(
|
||||
"Enter current password:",
|
||||
validate=lambda text: len(text) > 0 or "Current password required"
|
||||
).ask()
|
||||
|
||||
# New password
|
||||
new_password = questionary.password(
|
||||
"Enter new password:",
|
||||
validate=PasswordStrengthValidator(min_length=8)
|
||||
).ask()
|
||||
|
||||
# Ensure new password is different
|
||||
if new_password == old_password:
|
||||
print("\n❌ New password must be different from current password")
|
||||
return password_change_flow()
|
||||
|
||||
# Confirm new password
|
||||
confirm_password = questionary.password(
|
||||
"Confirm new password:",
|
||||
validate=lambda text: text == new_password or "Passwords do not match"
|
||||
).ask()
|
||||
|
||||
print(f"\n✅ Password changed successfully")
|
||||
print(f"🔒 New password strength: {calculate_password_strength(new_password)}")
|
||||
|
||||
return {'passwordChanged': True}
|
||||
|
||||
|
||||
def main():
|
||||
"""Run password prompt examples"""
|
||||
try:
|
||||
print("=== Password Prompt Examples ===")
|
||||
|
||||
# Example 1: Basic password prompts
|
||||
password_prompt_example()
|
||||
|
||||
# Example 2: Secure account setup
|
||||
secure_account_setup()
|
||||
|
||||
# Example 3: Database credentials
|
||||
database_credentials_setup()
|
||||
|
||||
# Example 4: API token setup
|
||||
api_token_setup()
|
||||
|
||||
# Example 5: Password change
|
||||
password_change_flow()
|
||||
|
||||
print("\n✅ Password prompt examples complete!")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n❌ User interrupted prompt")
|
||||
exit(1)
|
||||
except Exception as e:
|
||||
print(f"\n\n❌ Error: {e}")
|
||||
exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
220
skills/inquirer-patterns/templates/python/text_prompt.py
Normal file
220
skills/inquirer-patterns/templates/python/text_prompt.py
Normal file
@@ -0,0 +1,220 @@
|
||||
"""
|
||||
Text Input Prompt Template
|
||||
|
||||
Use for: Names, emails, URLs, paths, free-form text
|
||||
Features: Validation, default values, transform
|
||||
"""
|
||||
|
||||
import questionary
|
||||
import re
|
||||
from questionary import ValidationError, Validator
|
||||
|
||||
|
||||
class UsernameValidator(Validator):
|
||||
"""Validate username format"""
|
||||
|
||||
def validate(self, document):
|
||||
text = document.text
|
||||
if len(text) == 0:
|
||||
raise ValidationError(
|
||||
message='Username is required',
|
||||
cursor_position=len(text)
|
||||
)
|
||||
if len(text) < 3:
|
||||
raise ValidationError(
|
||||
message='Username must be at least 3 characters',
|
||||
cursor_position=len(text)
|
||||
)
|
||||
if not re.match(r'^[a-zA-Z0-9_-]+$', text):
|
||||
raise ValidationError(
|
||||
message='Username can only contain letters, numbers, hyphens, and underscores',
|
||||
cursor_position=len(text)
|
||||
)
|
||||
|
||||
|
||||
class EmailValidator(Validator):
|
||||
"""Validate email format"""
|
||||
|
||||
def validate(self, document):
|
||||
text = document.text
|
||||
email_regex = r'^[^\s@]+@[^\s@]+\.[^\s@]+$'
|
||||
if not re.match(email_regex, text):
|
||||
raise ValidationError(
|
||||
message='Invalid email address',
|
||||
cursor_position=len(text)
|
||||
)
|
||||
|
||||
|
||||
class URLValidator(Validator):
|
||||
"""Validate URL format"""
|
||||
|
||||
def validate(self, document):
|
||||
text = document.text
|
||||
if len(text) == 0:
|
||||
return # Optional field
|
||||
url_regex = r'^https?://.+'
|
||||
if not re.match(url_regex, text):
|
||||
raise ValidationError(
|
||||
message='Must be a valid URL (http:// or https://)',
|
||||
cursor_position=len(text)
|
||||
)
|
||||
|
||||
|
||||
class AgeValidator(Validator):
|
||||
"""Validate age range"""
|
||||
|
||||
def validate(self, document):
|
||||
text = document.text
|
||||
try:
|
||||
age = int(text)
|
||||
if age < 18:
|
||||
raise ValidationError(
|
||||
message='You must be at least 18 years old',
|
||||
cursor_position=len(text)
|
||||
)
|
||||
if age > 120:
|
||||
raise ValidationError(
|
||||
message='Please enter a realistic age',
|
||||
cursor_position=len(text)
|
||||
)
|
||||
except ValueError:
|
||||
raise ValidationError(
|
||||
message='Please enter a valid number',
|
||||
cursor_position=len(text)
|
||||
)
|
||||
|
||||
|
||||
class BioValidator(Validator):
|
||||
"""Validate bio length"""
|
||||
|
||||
def validate(self, document):
|
||||
text = document.text
|
||||
if len(text) > 200:
|
||||
raise ValidationError(
|
||||
message='Bio must be 200 characters or less',
|
||||
cursor_position=len(text)
|
||||
)
|
||||
|
||||
|
||||
def text_prompt_example():
|
||||
"""Example text input prompts with validation"""
|
||||
|
||||
print("\n📝 Text Input Example\n")
|
||||
|
||||
# Username input
|
||||
username = questionary.text(
|
||||
"Enter your username:",
|
||||
validate=UsernameValidator
|
||||
).ask()
|
||||
|
||||
# Email input
|
||||
email = questionary.text(
|
||||
"Enter your email:",
|
||||
validate=EmailValidator
|
||||
).ask()
|
||||
|
||||
# Website input (optional)
|
||||
website = questionary.text(
|
||||
"Enter your website (optional):",
|
||||
default="",
|
||||
validate=URLValidator
|
||||
).ask()
|
||||
|
||||
# Age input with conversion
|
||||
age_str = questionary.text(
|
||||
"Enter your age:",
|
||||
validate=AgeValidator
|
||||
).ask()
|
||||
age = int(age_str)
|
||||
|
||||
# Bio input
|
||||
bio = questionary.text(
|
||||
"Enter a short bio:",
|
||||
validate=BioValidator,
|
||||
multiline=False
|
||||
).ask()
|
||||
|
||||
answers = {
|
||||
'username': username.lower(),
|
||||
'email': email,
|
||||
'website': website,
|
||||
'age': age,
|
||||
'bio': bio
|
||||
}
|
||||
|
||||
print("\n✅ Answers received:")
|
||||
import json
|
||||
print(json.dumps(answers, indent=2))
|
||||
|
||||
return answers
|
||||
|
||||
|
||||
# Alternative: Using lambda validators
|
||||
def lambda_validator_example():
|
||||
"""Example using lambda validators for simpler cases"""
|
||||
|
||||
print("\n📝 Lambda Validator Example\n")
|
||||
|
||||
# Simple required field
|
||||
name = questionary.text(
|
||||
"Name:",
|
||||
validate=lambda text: len(text) > 0 or "Name is required"
|
||||
).ask()
|
||||
|
||||
# Email validation
|
||||
email = questionary.text(
|
||||
"Email:",
|
||||
validate=lambda text: bool(re.match(r'^[^\s@]+@[^\s@]+\.[^\s@]+$', text)) or "Invalid email"
|
||||
).ask()
|
||||
|
||||
# Numeric validation
|
||||
port = questionary.text(
|
||||
"Port number:",
|
||||
default="8000",
|
||||
validate=lambda text: text.isdigit() and 1 <= int(text) <= 65535 or "Invalid port (1-65535)"
|
||||
).ask()
|
||||
|
||||
# Path validation
|
||||
path = questionary.text(
|
||||
"Project path:",
|
||||
default="./my-project",
|
||||
validate=lambda text: len(text) > 0 or "Path is required"
|
||||
).ask()
|
||||
|
||||
answers = {
|
||||
'name': name,
|
||||
'email': email,
|
||||
'port': int(port),
|
||||
'path': path
|
||||
}
|
||||
|
||||
print("\n✅ Answers received:")
|
||||
import json
|
||||
print(json.dumps(answers, indent=2))
|
||||
|
||||
return answers
|
||||
|
||||
|
||||
def main():
|
||||
"""Run text prompt examples"""
|
||||
try:
|
||||
print("=== Text Prompt Examples ===")
|
||||
|
||||
# Example 1: Class-based validators
|
||||
text_prompt_example()
|
||||
|
||||
# Example 2: Lambda validators
|
||||
lambda_validator_example()
|
||||
|
||||
print("\n✅ Text prompt examples complete!")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n❌ User interrupted prompt")
|
||||
exit(1)
|
||||
except Exception as e:
|
||||
print(f"\n\n❌ Error: {e}")
|
||||
exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user