From 734d850ff7d713a18a77edc0f2facdaf7d9503ce Mon Sep 17 00:00:00 2001 From: Eyal Toledano Date: Sat, 22 Mar 2025 02:18:59 -0400 Subject: [PATCH] Improves init script visuals. --- package.json | 2 +- scripts/init.js | 159 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 113 insertions(+), 48 deletions(-) diff --git a/package.json b/package.json index 2a65c8a8..0effcd8a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "task-master-ai", - "version": "0.9.5", + "version": "0.9.6", "description": "A task management system for ambitious AI-driven development that doesn't overwhelm and confuse Cursor.", "main": "index.js", "type": "module", diff --git a/scripts/init.js b/scripts/init.js index 1e24bfed..4a632c8e 100755 --- a/scripts/init.js +++ b/scripts/init.js @@ -8,6 +8,10 @@ import { execSync } from 'child_process'; import readline from 'readline'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; +import chalk from 'chalk'; +import figlet from 'figlet'; +import boxen from 'boxen'; +import gradient from 'gradient-string'; // Debug information console.log('Node version:', process.version); @@ -17,42 +21,65 @@ console.log('Script path:', import.meta.url); const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); -// Define log levels and colors +// Define log levels const LOG_LEVELS = { debug: 0, info: 1, warn: 2, - error: 3 -}; - -const COLORS = { - reset: '\x1b[0m', - bright: '\x1b[1m', - dim: '\x1b[2m', - red: '\x1b[31m', - green: '\x1b[32m', - yellow: '\x1b[33m', - blue: '\x1b[34m', - magenta: '\x1b[35m', - cyan: '\x1b[36m' + error: 3, + success: 4 }; // Get log level from environment or default to info const LOG_LEVEL = process.env.LOG_LEVEL ? LOG_LEVELS[process.env.LOG_LEVEL.toLowerCase()] : LOG_LEVELS.info; -// Logging function -function log(level, ...args) { - const levelValue = LOG_LEVELS[level.toLowerCase()]; +// Create a color gradient for the banner +const coolGradient = gradient(['#00b4d8', '#0077b6', '#03045e']); +const warmGradient = gradient(['#fb8b24', '#e36414', '#9a031e']); + +// Display a fancy banner +function displayBanner() { + console.clear(); + const bannerText = figlet.textSync('Claude Task Master', { + font: 'Standard', + horizontalLayout: 'default', + verticalLayout: 'default' + }); - if (levelValue >= LOG_LEVEL) { - const prefix = { - debug: `${COLORS.dim}[DEBUG]${COLORS.reset}`, - info: `${COLORS.blue}[INFO]${COLORS.reset}`, - warn: `${COLORS.yellow}[WARN]${COLORS.reset}`, - error: `${COLORS.red}[ERROR]${COLORS.reset}` - }[level.toLowerCase()]; + console.log(coolGradient(bannerText)); + + console.log(boxen(chalk.white(`${chalk.bold('Initializing')} your new project`), { + padding: 1, + margin: { top: 0, bottom: 1 }, + borderStyle: 'round', + borderColor: 'cyan' + })); +} + +// Logging function with icons and colors +function log(level, ...args) { + const icons = { + debug: chalk.gray('🔍'), + info: chalk.blue('â„šī¸'), + warn: chalk.yellow('âš ī¸'), + error: chalk.red('❌'), + success: chalk.green('✅') + }; + + if (LOG_LEVELS[level] >= LOG_LEVEL) { + const icon = icons[level] || ''; - console.log(prefix, ...args); + if (level === 'error') { + console.error(icon, chalk.red(...args)); + } else if (level === 'warn') { + console.warn(icon, chalk.yellow(...args)); + } else if (level === 'success') { + console.log(icon, chalk.green(...args)); + } else if (level === 'info') { + console.log(icon, chalk.blue(...args)); + } else { + console.log(icon, ...args); + } } // Write to debug log if DEBUG=true @@ -125,6 +152,9 @@ function copyTemplateFile(templateName, targetPath, replacements = {}) { // Main function to initialize a new project async function initializeProject(options = {}) { + // Display the banner + displayBanner(); + // If options are provided, use them directly without prompting if (options.projectName && options.projectDescription) { const projectName = options.projectName; @@ -149,10 +179,10 @@ async function initializeProject(options = {}) { }); try { - const projectName = await promptQuestion(rl, 'Enter project name: '); - const projectDescription = await promptQuestion(rl, 'Enter project description: '); - const projectVersionInput = await promptQuestion(rl, 'Enter project version (default: 1.0.0): '); - const authorName = await promptQuestion(rl, 'Enter your name: '); + const projectName = await promptQuestion(rl, chalk.cyan('Enter project name: ')); + const projectDescription = await promptQuestion(rl, chalk.cyan('Enter project description: ')); + const projectVersionInput = await promptQuestion(rl, chalk.cyan('Enter project version (default: 1.0.0): ')); + const authorName = await promptQuestion(rl, chalk.cyan('Enter your name: ')); // Set default version if not provided const projectVersion = projectVersionInput.trim() ? projectVersionInput : '1.0.0'; @@ -210,10 +240,15 @@ function createProjectStructure(projectName, projectDescription, projectVersion, }, dependencies: { "@anthropic-ai/sdk": "^0.39.0", - "chalk": "^4.1.2", + "chalk": "^5.3.0", "commander": "^11.1.0", "dotenv": "^16.3.1", - "openai": "^4.86.1" + "openai": "^4.86.1", + "figlet": "^1.7.0", + "boxen": "^7.1.1", + "gradient-string": "^2.0.2", + "cli-table3": "^0.6.3", + "ora": "^7.0.1" } }; @@ -221,7 +256,7 @@ function createProjectStructure(projectName, projectDescription, projectVersion, path.join(targetDir, 'package.json'), JSON.stringify(packageJson, null, 2) ); - log('info', 'Created package.json'); + log('success', 'Created package.json'); // Copy template files with replacements const replacements = { @@ -262,35 +297,65 @@ function createProjectStructure(projectName, projectDescription, projectVersion, // Initialize git repository if git is available try { if (!fs.existsSync(path.join(targetDir, '.git'))) { + log('info', 'Initializing git repository...'); execSync('git init', { stdio: 'ignore' }); - log('info', 'Initialized git repository'); + log('success', 'Git repository initialized'); } } catch (error) { log('warn', 'Git not available, skipping repository initialization'); } // Run npm install automatically - log('info', 'Installing dependencies...'); + console.log(boxen(chalk.cyan('Installing dependencies...'), { + padding: 0.5, + margin: 0.5, + borderStyle: 'round', + borderColor: 'blue' + })); + try { execSync('npm install', { stdio: 'inherit', cwd: targetDir }); - log('info', `${COLORS.green}Dependencies installed successfully!${COLORS.reset}`); + log('success', 'Dependencies installed successfully!'); } catch (error) { log('error', 'Failed to install dependencies:', error.message); log('error', 'Please run npm install manually'); } - log('info', `${COLORS.green}${COLORS.bright}Project initialized successfully!${COLORS.reset}`); - log('info', ''); - log('info', 'Next steps:'); - log('info', '1. Rename .env.example to .env and add your ANTHROPIC_API_KEY and PERPLEXITY_API_KEY'); - log('info', '2. Discuss your idea with AI, and once ready ask for a PRD, and save it as PRD.txt in the /scripts directory'); - log('info', '3. Ask Cursor Agent to parse your PRD.txt and generate tasks'); - log('info', '└── You can also manually run `npm run parse-prd -- --input=` to generate tasks'); - log('info', '4. Ask Cursor to analyze the complexity of your tasks, which will generate a task-complexity-report.json file in /scripts for your review.'); - log('info', '5. Ask Cursor which task is next and it will determine what to start with based on task statuses and dependencies.'); - log('info', '6. Ask Cursor to expand any tasks that are too large in scope or complexity. The complexity report will be used to generate subtasks if it exists.'); - log('info', '7. Ship it!'); - log('info', '* Review the README.md file to learn how to use other commands via Cursor Agent.'); + // Display success message + console.log(boxen( + warmGradient.multiline(figlet.textSync('Success!', { font: 'Standard' })) + + '\n' + chalk.green('Project initialized successfully!'), + { + padding: 1, + margin: 1, + borderStyle: 'double', + borderColor: 'green' + } + )); + + // Display next steps in a nice box + console.log(boxen( + chalk.cyan.bold('Things you can now do:') + '\n\n' + + chalk.white('1. ') + chalk.yellow('Rename .env.example to .env and add your ANTHROPIC_API_KEY and PERPLEXITY_API_KEY') + '\n' + + chalk.white('2. ') + chalk.yellow('Discuss your idea with AI, and once ready ask for a PRD, and save it as PRD.txt') + '\n' + + chalk.white('3. ') + chalk.yellow('Ask Cursor Agent to parse your PRD.txt and generate tasks') + '\n' + + chalk.white(' └─ ') + chalk.dim('You can also run ') + chalk.cyan('npm run parse-prd -- --input=') + '\n' + + chalk.white('4. ') + chalk.yellow('Ask Cursor to analyze the complexity of your tasks') + '\n' + + chalk.white('5. ') + chalk.yellow('Ask Cursor which task is next to determine where to start') + '\n' + + chalk.white('6. ') + chalk.yellow('Ask Cursor to expand any complex tasks that are too large or complex.') + '\n' + + chalk.white('7. ') + chalk.yellow('Ask Cursor to set the status of a task, or multiple tasks. Use the task id from the task lists.') + '\n' + + chalk.white('8. ') + chalk.yellow('Ask Cursor to update all tasks from a specific task id based on new learnings or pivots in your project.') + '\n' + + chalk.white('9. ') + chalk.green.bold('Ship it!') + '\n\n' + + chalk.dim('* Review the README.md file to learn how to use other commands via Cursor Agent.'), + { + padding: 1, + margin: 1, + borderStyle: 'round', + borderColor: 'yellow', + title: 'Getting Started', + titleAlignment: 'center' + } + )); } // Run the initialization if this script is executed directly